How to add a custom grant type in OpenIddict
ITokenExtensionGrant
Create a class that inherits ITokenExtensionGrant
, and then register it with the framework.
In the MyTokenExtensionGrant
class below we try to get the token details, The ForbidResult
handles the failure case and SignInResult
returns a new token response, You can pass more parameters to implement business checks.
public class MyTokenExtensionGrant : ITokenExtensionGrant
{
public const string ExtensionGrantName = "MyTokenExtensionGrant";
public string Name => ExtensionGrantName;
public async Task<IActionResult> HandleAsync(ExtensionGrantContext context)
{
var userToken = context.Request.GetParameter("token").ToString();
if (string.IsNullOrEmpty(userToken))
{
return new ForbidResult(
new[] {OpenIddictServerAspNetCoreDefaults.AuthenticationScheme},
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictServerAspNetCoreConstants.Properties.Error] = OpenIddictConstants.Errors.InvalidRequest
}!));
}
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);
if (notification.IsRejected)
{
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
}));
}
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
}));
}
var userId = principal.FindUserId();
var userManager = context.HttpContext.RequestServices.GetRequiredService<IdentityUserManager>();
var user = await userManager.GetByIdAsync(userId.Value);
var userClaimsPrincipalFactory = context.HttpContext.RequestServices.GetRequiredService<IUserClaimsPrincipalFactory<IdentityUser>>();
var claimsPrincipal = await userClaimsPrincipalFactory.CreateAsync(user);
claimsPrincipal.SetScopes(principal.GetScopes());
claimsPrincipal.SetResources(await GetResourcesAsync(context, principal.GetScopes()));
//abp version < 7.3
await context.HttpContext.RequestServices.GetRequiredService<AbpOpenIddictClaimDestinationsManager>().SetAsync(claimsPrincipal);
//For abp version >= 7.3
await context.HttpContext.RequestServices.GetRequiredService<AbpOpenIddictClaimsPrincipalManager>().HandleAsync(context.Request, claimsPrincipal);
return new SignInResult(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, claimsPrincipal);
}
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;
}
}
public override void PreConfigureServices(ServiceConfigurationContext context)
{
//...
PreConfigure<OpenIddictServerBuilder>(builder =>
{
builder.Configure(openIddictServerOptions =>
{
openIddictServerOptions.GrantTypes.Add(MyTokenExtensionGrant.ExtensionGrantName);
});
});
//...
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
//...
Configure<AbpOpenIddictExtensionGrantsOptions>(options =>
{
options.Grants.Add(MyTokenExtensionGrant.ExtensionGrantName, new MyTokenExtensionGrant());
});
//...
}
Source code
https://github.com/abpframework/abp/commit/3210f138454697647689b4868c8d4b7b3da02d44
Comments
quehuo li 105 weeks ago
It would be better if the code could be explained. Thank you!
zxz4 105 weeks ago
very useful , thanks
Liming Ma 102 weeks ago
🙂
holyrong 102 weeks ago
var userToken = context.Request.GetParameter("token").ToString();
how can we generate the token in the method: public async Task<IActionResult> HandleAsync(ExtensionGrantContext context)
we only pass username and password from the context,after verified the password is ok,how to generate token and return to client in the method HandleAsnyc?
@maliming
szilardd 100 weeks ago
Not exactly sure that it is right, but it works like this for me:
https://gist.github.com/szilardd/3ba1169d11d99b0ec5181253763775d0
Instead of username and password, I send only the user id
snow 40 weeks ago
send only the user id:
gist.github.com/snowchenlei/101e983bbd18a90de71ca30d330f9969
comment not support url.please add https://
raymondbu95 18 weeks ago
get unsupported grant type, why?
Liming Ma 17 weeks ago
hi
Have you added the grant type to your client's permissions?
https://github.com/abpframework/abp/blob/3210f138454697647689b4868c8d4b7b3da02d44/modules/openiddict/app/OpenIddict.Demo.Server/EntityFrameworkCore/ServerDataSeedContributor.cs#L79
somikayani11@gmail.com 14 weeks ago
{ "error": "unauthorized_client", "error_description": "This client application is not allowed to use the specified grant type.", "error_uri": "documentation.openiddict.com/errors/ID2064" }
Liming Ma 14 weeks ago
hi
Have you added the grant type to your client's permissions?
https://github.com/abpframework/abp/blob/3210f138454697647689b4868c8d4b7b3da02d44/modules/openiddict/app/OpenIddict.Demo.Server/EntityFrameworkCore/ServerDataSeedContributor.cs#L79