136 lines
No EOL
4.8 KiB
C#
136 lines
No EOL
4.8 KiB
C#
using IdentityShroud.Api.Apis;
|
|
using IdentityShroud.Api.Apis.ISResults;
|
|
using IdentityShroud.Api.Helpers;
|
|
using IdentityShroud.Api.Mappers;
|
|
using IdentityShroud.Core.Contracts;
|
|
using IdentityShroud.Core.Messages;
|
|
using IdentityShroud.Core.Model;
|
|
using IdentityShroud.Core.Services.OpenId;
|
|
using Microsoft.AspNetCore.Http.HttpResults;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
|
|
namespace IdentityShroud.Api;
|
|
|
|
public static class OpenIdEndpoints
|
|
{
|
|
// openid: auth/realms/{realmSlug}/.well-known/openid-configuration
|
|
// openid: auth/realms/{realmSlug}/openid-connect/(auth|token|jwks)
|
|
public static void MapEndpoints(this IEndpointRouteBuilder erp)
|
|
{
|
|
var realmsGroup = erp.MapGroup("/auth/realms");
|
|
|
|
var realmSlugGroup = realmsGroup.MapGroup("{realmSlug}")
|
|
.AddEndpointFilter<RealmSlugValidationFilter>();
|
|
realmSlugGroup.MapGet(".well-known/openid-configuration", GetOpenIdConfiguration);
|
|
|
|
var openidConnect = realmSlugGroup.MapGroup("openid-connect");
|
|
openidConnect.MapPost("auth", OpenIdConnectAuth);
|
|
openidConnect.MapPost("token", OpenIdConnectToken);
|
|
openidConnect.MapGet("jwks", OpenIdConnectJwks);
|
|
}
|
|
|
|
private static async Task<JsonHttpResult<OpenIdConfiguration>> GetOpenIdConfiguration(
|
|
string realmSlug,
|
|
[FromServices]IRealmService realmService,
|
|
HttpContext context)
|
|
{
|
|
Realm realm = context.GetValidatedRealm();
|
|
|
|
var s = $"{context.Request.Scheme}://{context.Request.Host}{context.Request.Path}";
|
|
var searchString = $"realms/{realmSlug}";
|
|
int index = s.IndexOf(searchString, StringComparison.OrdinalIgnoreCase);
|
|
string baseUri = s.Substring(0, index + searchString.Length);
|
|
|
|
return TypedResults.Json(new OpenIdConfiguration()
|
|
{
|
|
AuthorizationEndpoint = baseUri + "/openid-connect/auth",
|
|
TokenEndpoint = baseUri + "/openid-connect/token",
|
|
Issuer = baseUri,
|
|
JwksUri = baseUri + "/openid-connect/jwks",
|
|
}, AppJsonSerializerContext.Default.OpenIdConfiguration);
|
|
}
|
|
|
|
private static async Task<Results<Ok<JsonWebKeySet>, BadRequest>> OpenIdConnectJwks(
|
|
string realmSlug,
|
|
[FromServices]IRealmService realmService,
|
|
[FromServices]KeyMapper keyMapper,
|
|
HttpContext context)
|
|
{
|
|
Realm realm = context.GetValidatedRealm();
|
|
await realmService.LoadActiveKeys(realm);
|
|
return TypedResults.Ok(keyMapper.KeyListToJsonWebKeySet(realm.TokenSigningKeys));
|
|
}
|
|
|
|
private static async Task<Results<
|
|
Ok<TokenResponse>,
|
|
BadRequest<ErrorDto>,
|
|
ISUnauthorizedHttpResult
|
|
>> OpenIdConnectToken(
|
|
string realmSlug,
|
|
[FromServices] IClientService clientService,
|
|
HttpContext context,
|
|
CancellationToken ct)
|
|
{
|
|
IFormCollection form = await context.Request.ReadFormAsync();
|
|
|
|
string grantType = form["grant_type"].ToString();
|
|
string clientId = form["client_id"].ToString();
|
|
string scope = form["scope"].ToString();
|
|
|
|
if (grantType == "client_credentials")
|
|
{
|
|
string? clientSecret = null;
|
|
bool withAuthHeader = false;
|
|
if (HeaderHelpers.TryGetBasicAuth(context, out string? user, out string? password))
|
|
{
|
|
withAuthHeader = true;
|
|
clientId = user;
|
|
clientSecret = password;
|
|
}
|
|
clientSecret ??= form["client_secret"].ToString();
|
|
|
|
if (string.IsNullOrEmpty(clientId) ||
|
|
string.IsNullOrEmpty(clientSecret))
|
|
{
|
|
return CreateBadRequest("invalid_request");
|
|
}
|
|
|
|
Realm realm = context.GetValidatedRealm();
|
|
Client? client = await clientService.GetByClientId(realm.Id, clientId, ct);
|
|
if (client is null)
|
|
{
|
|
if (withAuthHeader)
|
|
{
|
|
return new ISUnauthorizedHttpResult([$"Basic realm=\"{realm.Slug}\""]);
|
|
}
|
|
return CreateBadRequest("invalid_client");
|
|
}
|
|
|
|
if (!client.AllowClientCredentialsFlow)
|
|
return CreateBadRequest("unauthorized_client");
|
|
|
|
}
|
|
else
|
|
return CreateBadRequest("unsupported_grant_type");
|
|
|
|
context.Response.Headers.CacheControl = "no-store";
|
|
context.Response.Headers.Pragma = "no-cache";
|
|
|
|
return TypedResults.Ok(new TokenResponse()
|
|
{
|
|
AccessToken = "token",
|
|
TokenType = "bearer",
|
|
ExpiresIn = 3600,
|
|
});
|
|
}
|
|
|
|
private static BadRequest<ErrorDto> CreateBadRequest(string error) =>
|
|
TypedResults.BadRequest(new ErrorDto(error));
|
|
|
|
|
|
|
|
private static Task OpenIdConnectAuth(HttpContext context)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
} |