IdentityShroud/IdentityShroud.Api/Apis/OpenIdEndpoints.cs
2026-08-18 07:42:51 +02:00

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",
});
}
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();
}
}