Skip to content

Commit

Permalink
FEAT: Making policies optional to be used
Browse files Browse the repository at this point in the history
  • Loading branch information
Felipe Mattioli dos Santos committed Jul 10, 2024
1 parent 0dfb9cc commit 8c04417
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 105 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,122 +17,128 @@ public static IServiceCollection AddKeyCloakAuth(this IServiceCollection service
var httpClient = new HttpClient();
var tokenHandler = new JwtSecurityTokenHandler();

services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddKeycloakWebApi(
options =>
{
options.Resource = authSettings.Resource!;
options.AuthServerUrl = authSettings.AuthServerUrl;
options.VerifyTokenAudience = true;
},
options =>
{
options.Events = new JwtBearerEvents
services.AddSingleton<JwtSecurityTokenHandler>()
.AddSingleton(authSettings)
.AddScoped<IAuthService, AuthService>()
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddKeycloakWebApi(
options =>
{
options.Resource = authSettings.Resource!;
options.AuthServerUrl = authSettings.AuthServerUrl;
options.VerifyTokenAudience = true;
},
options =>
{
OnMessageReceived = async context =>
options.Events = new JwtBearerEvents
{
try
OnMessageReceived = async context =>
{
var tokenJwt = context.Request.Headers.Authorization.FirstOrDefault();
if (string.IsNullOrEmpty(tokenJwt))
try
{
context.HttpContext.Items["AuthError"] = "Invalid JWT token provided! Please check. ";
context.HttpContext.Items["AuthStatusCode"] = 401;
return;
}
var tokenJwt = context.Request.Headers.Authorization.FirstOrDefault();
var bearerToken = tokenJwt.Replace("Bearer ", "");
var tokenInfos = tokenHandler.ReadJwtToken(tokenJwt.Replace("Bearer ", ""));
var tenantNumber = tokenInfos.Claims.FirstOrDefault(c => c.Type == "tenant")?.Value;
var tenantRealm = authSettings.Realms.FirstOrDefault(realm => realm.Name == tenantNumber);
if (string.IsNullOrEmpty(tokenJwt))
{
context.HttpContext.Items["AuthError"] = "Invalid JWT token provided! Please check. ";
context.HttpContext.Items["AuthStatusCode"] = 401;
return;
}
if (tenantRealm is null)
{
context.HttpContext.Items["AuthError"] = "This token don't belongs to valid tenant. Please check!";
context.HttpContext.Items["AuthStatusCode"] = 401;
context.NoResult();
return;
}
var bearerToken = tokenJwt.Replace("Bearer ", "");
var tokenInfos = tokenHandler.ReadJwtToken(tokenJwt.Replace("Bearer ", ""));
var tenantNumber = tokenInfos.Claims.FirstOrDefault(c => c.Type == "tenant")?.Value;
var tenantRealm = authSettings.Realms.FirstOrDefault(realm => realm.Name == tenantNumber);
var audience = tokenInfos.Claims.FirstOrDefault(c => c.Type == "aud")?.Value;
if (string.IsNullOrEmpty(audience))
{
context.HttpContext.Items["AuthError"] = "Invalid scope provided! Please, check the scopes provided!";
context.HttpContext.Items["AuthStatusCode"] = 403;
context.NoResult();
return;
}
if (tenantRealm is null)
{
context.HttpContext.Items["AuthError"] = "This token don't belongs to valid tenant. Please check!";
context.HttpContext.Items["AuthStatusCode"] = 401;
context.NoResult();
return;
}
var jwksUrl = $"{tenantRealm.Issuer}/protocol/openid-connect/certs";
var audience = tokenInfos.Claims.FirstOrDefault(c => c.Type == "aud")?.Value;
if (string.IsNullOrEmpty(audience))
{
context.HttpContext.Items["AuthError"] = "Invalid scope provided! Please, check the scopes provided!";
context.HttpContext.Items["AuthStatusCode"] = 403;
context.NoResult();
return;
}
var jwks = await httpClient.GetStringAsync(jwksUrl);
var jsonWebKeySet = new JsonWebKeySet(jwks);
var jwksUrl = $"{tenantRealm.Issuer}/protocol/openid-connect/certs";
var tokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = tenantRealm.Issuer,
ValidateAudience = true,
ValidAudience = tenantRealm.Audience,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
IssuerSigningKeys = jsonWebKeySet.Keys
};
var jwks = await httpClient.GetStringAsync(jwksUrl);
var jsonWebKeySet = new JsonWebKeySet(jwks);
var claims = tokenHandler.ValidateToken(bearerToken, tokenValidationParameters, out var validatedToken);
context.Principal = claims;
context.Success();
}
catch (Exception e)
{
context.Response.StatusCode = 500;
context.HttpContext.Items["AuthError"] = "The following error occurs during the authentication process: " + e.Message;
context.Fail("");
}
},
OnAuthenticationFailed = async context =>
{
var errorDescription = context.Exception.Message;
context.Response.StatusCode = 401;
context.Response.ContentType = "application/json";
await context.Response.WriteAsJsonAsync(errorDescription);
},
OnChallenge = async context =>
{
if (!context.Response.HasStarted)
var tokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = tenantRealm.Issuer,
ValidateAudience = true,
ValidAudience = tenantRealm.Audience,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
IssuerSigningKeys = jsonWebKeySet.Keys
};
var claims = tokenHandler.ValidateToken(bearerToken, tokenValidationParameters, out var validatedToken);
context.Principal = claims;
context.Success();
}
catch (Exception e)
{
context.Response.StatusCode = 500;
context.HttpContext.Items["AuthError"] = "The following error occurs during the authentication process: " + e.Message;
context.Fail("");
}
},
OnAuthenticationFailed = async context =>
{
var errorMessage = context.HttpContext.Items["AuthError"] as string ?? "Authentication failed!";
var statusCode = context.HttpContext.Items["AuthStatusCode"] as int? ?? 401;
var responseMessage = new { Message = errorMessage };
context.Response.StatusCode = statusCode;
var errorDescription = context.Exception.Message;
context.Response.StatusCode = 401;
context.Response.ContentType = "application/json";
await context.Response.WriteAsJsonAsync(responseMessage);
await context.Response.WriteAsJsonAsync(errorDescription);
},
OnChallenge = async context =>
{
if (!context.Response.HasStarted)
{
var errorMessage = context.HttpContext.Items["AuthError"] as string ?? "Authentication failed!";
var statusCode = context.HttpContext.Items["AuthStatusCode"] as int? ?? 401;
var responseMessage = new { Message = errorMessage };
context.Response.StatusCode = statusCode;
context.Response.ContentType = "application/json";
await context.Response.WriteAsJsonAsync(responseMessage);
}
context.HandleResponse();
}
context.HandleResponse();
}
};
}
);
};
}
);

services
.AddAuthorization()
.AddKeycloakAuthorization()
.AddAuthorizationBuilder()
.AddPolicy(
authSettings.PolicyName!,
policy =>
{
policy.RequireResourceRolesForClient(
authSettings!.Resource!,
authSettings!.Roles!.ToArray());
}
);
.AddKeycloakAuthorization();

services.AddSingleton<JwtSecurityTokenHandler>()
.AddSingleton(authSettings)
.AddScoped<IAuthService, AuthService>();
if (!string.IsNullOrEmpty(authSettings?.PolicyName))
{
services.AddAuthorizationBuilder()
.AddPolicy(
authSettings.PolicyName,
policy =>
{
policy.RequireResourceRolesForClient(
authSettings.Resource!,
authSettings.Roles!.ToArray());
}
);
}
else
{
services.AddAuthorizationBuilder();
}

return services;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
{
public class AuthSettings
{
public string? ClientId { get; set; }
public string? ClientSecret { get; set; }
public string? Resource { get; set; }
public string? AuthServerUrl { get; set; }
public required string ClientId { get; set; }
public required string ClientSecret { get; set; }
public required string Resource { get; set; }
public required string AuthServerUrl { get; set; }
public required IEnumerable<Realm> Realms { get; set; }
public string? PolicyName { get; set; }
public IEnumerable<string> Roles { get; set; } = [];
public IEnumerable<string> Scopes { get; set; } = [];
public IEnumerable<Realm> Realms { get; set; } = [];
public IEnumerable<string>? Roles { get; set; } = [];
public IEnumerable<string>? Scopes { get; set; } = [];
}

public class Realm
Expand Down

0 comments on commit 8c04417

Please sign in to comment.