changes: Working SSo flow
This commit is contained in:
@@ -82,12 +82,29 @@ public class ExternalAuthenticationManager : DomainService
|
||||
|
||||
// 3. Validate JWT token
|
||||
var claims = await ValidateTokenAsync(provider, idToken);
|
||||
var sub = claims.FirstOrDefault(c => c.Type == "sub")?.Value;
|
||||
var email = claims.FirstOrDefault(c => c.Type == "email")?.Value;
|
||||
|
||||
// Log all claims for debugging
|
||||
Logger.Info("Claims received from token:");
|
||||
foreach (var claim in claims)
|
||||
{
|
||||
Logger.Info($" {claim.Type} = {claim.Value}");
|
||||
}
|
||||
|
||||
// Try to find subject identifier (sub, uid, user_id, etc.)
|
||||
var sub = claims.FirstOrDefault(c => c.Type == "sub")?.Value
|
||||
?? claims.FirstOrDefault(c => c.Type == "uid")?.Value
|
||||
?? claims.FirstOrDefault(c => c.Type == "user_id")?.Value
|
||||
?? claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value;
|
||||
|
||||
// Try to find email using multiple formats
|
||||
var email = claims.FirstOrDefault(c => c.Type == "email")?.Value
|
||||
?? claims.FirstOrDefault(c => c.Type == ClaimTypes.Email)?.Value
|
||||
?? claims.FirstOrDefault(c => c.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress")?.Value;
|
||||
|
||||
if (string.IsNullOrEmpty(sub))
|
||||
{
|
||||
throw new UserFriendlyException("Invalid token: 'sub' claim not found");
|
||||
Logger.Error("Subject claim not found. Available claims: " + string.Join(", ", claims.Select(c => c.Type)));
|
||||
throw new UserFriendlyException("Invalid token: 'sub' claim not found. Check server logs for details.");
|
||||
}
|
||||
|
||||
// 4. Find or create user
|
||||
@@ -337,7 +354,23 @@ public class ExternalAuthenticationManager : DomainService
|
||||
mappedType = claimType;
|
||||
}
|
||||
|
||||
return claims.FirstOrDefault(c => c.Type == mappedType)?.Value;
|
||||
// Try to find the claim by mapped type or standard type
|
||||
var value = claims.FirstOrDefault(c => c.Type == mappedType)?.Value;
|
||||
if (!string.IsNullOrEmpty(value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
// Fallback: Try common Microsoft claim type mappings
|
||||
return claimType switch
|
||||
{
|
||||
"email" => claims.FirstOrDefault(c => c.Type == ClaimTypes.Email)?.Value,
|
||||
"name" => claims.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value,
|
||||
"given_name" => claims.FirstOrDefault(c => c.Type == ClaimTypes.GivenName)?.Value,
|
||||
"family_name" => claims.FirstOrDefault(c => c.Type == ClaimTypes.Surname)?.Value,
|
||||
"preferred_username" => claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value,
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
|
||||
private int GetTokenExpiration(string idToken)
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace ASPBaseOIDC.Controllers
|
||||
|
||||
/// <summary>
|
||||
/// Authenticate with external OIDC/OAuth2 provider (Authentik, Keycloak, etc.)
|
||||
/// Passthrough approach: validates external token and returns it as-is
|
||||
/// Validates external token and returns backend's own JWT
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
[AbpAllowAnonymous]
|
||||
@@ -77,12 +77,26 @@ namespace ASPBaseOIDC.Controllers
|
||||
AbpSession.TenantId
|
||||
);
|
||||
|
||||
// Return original external token (passthrough approach)
|
||||
// Create claims identity for the user (same as local login)
|
||||
var identity = new ClaimsIdentity();
|
||||
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, result.User.Id.ToString()));
|
||||
identity.AddClaim(new Claim(ClaimTypes.Name, result.User.UserName));
|
||||
identity.AddClaim(new Claim(ClaimTypes.Email, result.User.EmailAddress));
|
||||
|
||||
// Add tenant claim if applicable
|
||||
if (result.User.TenantId.HasValue)
|
||||
{
|
||||
identity.AddClaim(new Claim(AbpClaimTypes.TenantId, result.User.TenantId.Value.ToString()));
|
||||
}
|
||||
|
||||
// Generate backend's own JWT token
|
||||
var accessToken = CreateAccessToken(CreateJwtClaims(identity));
|
||||
|
||||
return new AuthenticateResultModel
|
||||
{
|
||||
AccessToken = result.AccessToken, // Passthrough external token
|
||||
EncryptedAccessToken = GetEncryptedAccessToken(result.AccessToken),
|
||||
ExpireInSeconds = result.ExpiresIn,
|
||||
AccessToken = accessToken, // Backend's own JWT
|
||||
EncryptedAccessToken = GetEncryptedAccessToken(accessToken),
|
||||
ExpireInSeconds = (int)_configuration.Expiration.TotalSeconds,
|
||||
UserId = result.User.Id
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "https://localhost:44313/",
|
||||
"sslPort": 44313
|
||||
"applicationUrl": "http://localhost:44312/"
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
@@ -18,8 +17,7 @@
|
||||
"ASPBaseOIDC.Web.Host": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "https://localhost:44313/",
|
||||
"sslPort": 44313,
|
||||
"applicationUrl": "http://localhost:44312/",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"App": {
|
||||
"ServerRootAddress": "https://localhost:44311/",
|
||||
"ClientRootAddress": "http://localhost:4200/",
|
||||
"CorsOrigins": "http://localhost:4200,http://localhost:8080,http://localhost:8081,http://localhost:3000"
|
||||
"CorsOrigins": "http://localhost:4200,http://localhost:8080,http://localhost:8081,http://localhost:3000,http://localhost:3001"
|
||||
},
|
||||
"Authentication": {
|
||||
"JwtBearer": {
|
||||
@@ -26,7 +26,7 @@
|
||||
"Kestrel": {
|
||||
"Endpoints": {
|
||||
"Http": {
|
||||
"Url": "https://localhost:44311/"
|
||||
"Url": "http://localhost:44311/"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user