✨ Login with Apple
This commit is contained in:
		| @@ -1,5 +1,8 @@ | ||||
| using DysonNetwork.Sphere.Account; | ||||
| using Microsoft.AspNetCore.Mvc; | ||||
| using Microsoft.Extensions.DependencyInjection; | ||||
| using Microsoft.EntityFrameworkCore; | ||||
| using Microsoft.IdentityModel.Tokens; | ||||
| using NodaTime; | ||||
|  | ||||
| namespace DysonNetwork.Sphere.Auth.OpenId; | ||||
|  | ||||
| @@ -8,7 +11,7 @@ namespace DysonNetwork.Sphere.Auth.OpenId; | ||||
| public class OidcController( | ||||
|     IServiceProvider serviceProvider, | ||||
|     AppDatabase db, | ||||
|     Account.AccountService accountService, | ||||
|     AccountService accounts, | ||||
|     AuthService authService | ||||
| ) | ||||
|     : ControllerBase | ||||
| @@ -35,6 +38,51 @@ public class OidcController( | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// <summary> | ||||
|     /// Mobile Apple Sign In endpoint | ||||
|     /// Handles Apple authentication directly from mobile apps | ||||
|     /// </summary> | ||||
|     [HttpPost("apple/mobile")] | ||||
|     public async Task<ActionResult<AuthController.TokenExchangeResponse>> AppleMobileSignIn([FromBody] AppleMobileSignInRequest request) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             // Get Apple OIDC service | ||||
|             if (GetOidcService("apple") is not AppleOidcService appleService) | ||||
|                 return StatusCode(503, "Apple OIDC service not available"); | ||||
|  | ||||
|             // Prepare callback data for processing | ||||
|             var callbackData = new OidcCallbackData | ||||
|             { | ||||
|                 IdToken = request.IdentityToken, | ||||
|                 Code = request.AuthorizationCode, | ||||
|             }; | ||||
|  | ||||
|             // Process the authentication | ||||
|             var userInfo = await appleService.ProcessCallbackAsync(callbackData); | ||||
|  | ||||
|             // Find or create user account using existing logic | ||||
|             var account = await FindOrCreateAccount(userInfo, "apple"); | ||||
|  | ||||
|             // Create session using the OIDC service | ||||
|             var session = await appleService.CreateSessionForUserAsync(userInfo, account); | ||||
|  | ||||
|             // Generate token using existing auth service | ||||
|             var token = authService.CreateToken(session); | ||||
|  | ||||
|             return Ok(new AuthController.TokenExchangeResponse { Token = token }); | ||||
|         } | ||||
|         catch (SecurityTokenValidationException ex) | ||||
|         { | ||||
|             return Unauthorized($"Invalid identity token: {ex.Message}"); | ||||
|         } | ||||
|         catch (Exception ex) | ||||
|         { | ||||
|             // Log the error | ||||
|             return StatusCode(500, $"Authentication failed: {ex.Message}"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private OidcService GetOidcService(string provider) | ||||
|     { | ||||
|         return provider.ToLower() switch | ||||
| @@ -45,4 +93,51 @@ public class OidcController( | ||||
|             _ => throw new ArgumentException($"Unsupported provider: {provider}") | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     private async Task<Account.Account> FindOrCreateAccount(OidcUserInfo userInfo, string provider) | ||||
|     { | ||||
|         if (string.IsNullOrEmpty(userInfo.Email)) | ||||
|             throw new ArgumentException("Email is required for account creation"); | ||||
|  | ||||
|         // Check if account exists by email | ||||
|         var existingAccount = await accounts.LookupAccount(userInfo.Email); | ||||
|         if (existingAccount != null) | ||||
|         { | ||||
|             // Check if this provider connection already exists | ||||
|             var existingConnection = await db.AccountConnections | ||||
|                 .FirstOrDefaultAsync(c => c.AccountId == existingAccount.Id && | ||||
|                                           c.Provider == provider && | ||||
|                                           c.ProvidedIdentifier == userInfo.UserId); | ||||
|  | ||||
|             // If no connection exists, create one | ||||
|             if (existingConnection != null) return existingAccount; | ||||
|             var connection = new AccountConnection | ||||
|             { | ||||
|                 AccountId = existingAccount.Id, | ||||
|                 Provider = provider, | ||||
|                 ProvidedIdentifier = userInfo.UserId!, | ||||
|             }; | ||||
|  | ||||
|             db.AccountConnections.Add(connection); | ||||
|             await db.SaveChangesAsync(); | ||||
|  | ||||
|             return existingAccount; | ||||
|         } | ||||
|  | ||||
|         // Create new account using the AccountService | ||||
|         var newAccount = await accounts.CreateAccount(userInfo); | ||||
|  | ||||
|         // Create the provider connection | ||||
|         var newConnection = new AccountConnection | ||||
|         { | ||||
|             AccountId = newAccount.Id, | ||||
|             Provider = provider, | ||||
|             ProvidedIdentifier = userInfo.UserId!, | ||||
|         }; | ||||
|  | ||||
|         db.AccountConnections.Add(newConnection); | ||||
|         await db.SaveChangesAsync(); | ||||
|  | ||||
|         return newAccount; | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user