Created
January 12, 2023 12:58
-
-
Save szilardd/3ba1169d11d99b0ec5181253763775d0 to your computer and use it in GitHub Desktop.
How to add a custom grant type in OpenIddict for existing user
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.AspNetCore.Authentication; | |
using Microsoft.AspNetCore.Identity; | |
using Microsoft.AspNetCore.Mvc; | |
using Microsoft.Extensions.DependencyInjection; | |
using Microsoft.Extensions.Options; | |
using Microsoft.IdentityModel.Tokens; | |
using OpenIddict.Abstractions; | |
using OpenIddict.Server; | |
using OpenIddict.Server.AspNetCore; | |
using System; | |
using System.Collections.Generic; | |
using System.Collections.Immutable; | |
using System.Linq; | |
using System.Threading.Tasks; | |
using Volo.Abp.Identity; | |
using Volo.Abp.OpenIddict; | |
using Volo.Abp.OpenIddict.ExtensionGrantTypes; | |
using Volo.Abp.Security.Claims; | |
namespace MyProject | |
{ | |
public class MyTokenExtensionGrant : IExtensionGrant | |
{ | |
public string Name => "MyTokenExtensionGrant"; | |
public async Task<IActionResult> HandleAsync(ExtensionGrantContext context) | |
{ | |
// get user id from request | |
var userId = context.Request.GetParameter("uid")?.Value?.ToString(); | |
if (string.IsNullOrEmpty(userId)) | |
{ | |
return new ForbidResult | |
( | |
new[] { OpenIddictServerAspNetCoreDefaults.AuthenticationScheme }, | |
properties: new AuthenticationProperties(new Dictionary<string, string> | |
{ | |
[OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant | |
} | |
)); | |
} | |
// retrieve user | |
var userManager = context.HttpContext.RequestServices.GetRequiredService<IdentityUserManager>(); | |
var user = await userManager.FindByIdAsync(userId); | |
if (user == null) | |
{ | |
return new ForbidResult | |
( | |
new[] { OpenIddictServerAspNetCoreDefaults.AuthenticationScheme }, | |
properties: new AuthenticationProperties(new Dictionary<string, string> | |
{ | |
[OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidGrant | |
} | |
)); | |
} | |
// generate temporary user token | |
var oidcOptions = context.HttpContext.RequestServices.GetRequiredService<IOptionsMonitor<OpenIddictServerOptions>>(); | |
var options = oidcOptions.CurrentValue; | |
var descriptor = new SecurityTokenDescriptor | |
{ | |
Claims = new Dictionary<string, object> | |
{ | |
{ "sub", user.Id }, | |
{ "scope", context.Request.GetParameter("scope")?.Value?.ToString() }, | |
}, | |
EncryptingCredentials = options.DisableAccessTokenEncryption | |
? null | |
: options.EncryptionCredentials.First(), | |
Expires = null, // recommended to set this | |
IssuedAt = DateTime.UtcNow, | |
SigningCredentials = options.SigningCredentials.First(), | |
TokenType = OpenIddictConstants.JsonWebTokenTypes.AccessToken | |
}; | |
var userToken = options.JsonWebTokenHandler.CreateToken(descriptor); | |
// authenticate with temporary token | |
var transaction = await context.HttpContext.RequestServices.GetRequiredService<IOpenIddictServerFactory>().CreateTransactionAsync(); | |
transaction.EndpointType = OpenIddictServerEndpointType.Introspection; | |
transaction.Request = new OpenIddictRequest | |
{ | |
ClientId = context.Request.ClientId, | |
ClientSecret = context.Request.ClientSecret, | |
Token = userToken | |
}; | |
var notification = new OpenIddictServerEvents.ProcessAuthenticationContext(transaction); | |
var dispatcher = context.HttpContext.RequestServices.GetRequiredService<IOpenIddictServerDispatcher>(); | |
await dispatcher.DispatchAsync(notification); | |
var principal = notification.GenericTokenPrincipal; | |
if (principal == null) | |
{ | |
return new ForbidResult( | |
new[] { OpenIddictServerAspNetCoreDefaults.AuthenticationScheme }, | |
properties: new AuthenticationProperties(new Dictionary<string, string> | |
{ | |
[OpenIddictServerAspNetCoreConstants.Properties.Error] = notification.Error ?? OpenIddictConstants.Errors.InvalidRequest, | |
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = notification.ErrorDescription, | |
[OpenIddictServerAspNetCoreConstants.Properties.ErrorUri] = notification.ErrorUri | |
})); | |
} | |
// retrieve generic user claims | |
var userClaimsPrincipalFactory = context.HttpContext.RequestServices.GetRequiredService<IUserClaimsPrincipalFactory<Volo.Abp.Identity.IdentityUser>>(); | |
var claimsPrincipal = await userClaimsPrincipalFactory.CreateAsync(user); | |
claimsPrincipal.SetScopes(principal.GetScopes()); | |
claimsPrincipal.SetResources(await GetResourcesAsync(context, principal.GetScopes())); | |
// retrieve abp user claims | |
var abpClaimsPrincipalFactory = context.HttpContext.RequestServices.GetRequiredService<IAbpClaimsPrincipalFactory>(); | |
var abpClaimsPrincipal = await abpClaimsPrincipalFactory.CreateAsync(claimsPrincipal); | |
await context.HttpContext.RequestServices.GetRequiredService<AbpOpenIddictClaimDestinationsManager>().SetAsync(abpClaimsPrincipal); | |
return new Microsoft.AspNetCore.Mvc.SignInResult(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, abpClaimsPrincipal); | |
} | |
private async Task<IEnumerable<string>> GetResourcesAsync(ExtensionGrantContext context, ImmutableArray<string> scopes) | |
{ | |
var resources = new List<string>(); | |
if (!scopes.Any()) | |
{ | |
return resources; | |
} | |
await foreach (var resource in context.HttpContext.RequestServices.GetRequiredService<IOpenIddictScopeManager>().ListResourcesAsync(scopes)) | |
{ | |
resources.Add(resource); | |
} | |
return resources; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment