|
|
|
@ -23,519 +23,518 @@ using OpenIddict.Abstractions; |
|
|
|
using OpenIddict.Server.AspNetCore; |
|
|
|
using static OpenIddict.Abstractions.OpenIddictConstants; |
|
|
|
|
|
|
|
namespace Mvc.Server |
|
|
|
namespace Mvc.Server; |
|
|
|
|
|
|
|
public class AuthorizationController : Controller |
|
|
|
{ |
|
|
|
public class AuthorizationController : Controller |
|
|
|
private readonly IOpenIddictApplicationManager _applicationManager; |
|
|
|
private readonly IOpenIddictAuthorizationManager _authorizationManager; |
|
|
|
private readonly IOpenIddictScopeManager _scopeManager; |
|
|
|
private readonly SignInManager<ApplicationUser> _signInManager; |
|
|
|
private readonly UserManager<ApplicationUser> _userManager; |
|
|
|
|
|
|
|
public AuthorizationController( |
|
|
|
IOpenIddictApplicationManager applicationManager, |
|
|
|
IOpenIddictAuthorizationManager authorizationManager, |
|
|
|
IOpenIddictScopeManager scopeManager, |
|
|
|
SignInManager<ApplicationUser> signInManager, |
|
|
|
UserManager<ApplicationUser> userManager) |
|
|
|
{ |
|
|
|
private readonly IOpenIddictApplicationManager _applicationManager; |
|
|
|
private readonly IOpenIddictAuthorizationManager _authorizationManager; |
|
|
|
private readonly IOpenIddictScopeManager _scopeManager; |
|
|
|
private readonly SignInManager<ApplicationUser> _signInManager; |
|
|
|
private readonly UserManager<ApplicationUser> _userManager; |
|
|
|
|
|
|
|
public AuthorizationController( |
|
|
|
IOpenIddictApplicationManager applicationManager, |
|
|
|
IOpenIddictAuthorizationManager authorizationManager, |
|
|
|
IOpenIddictScopeManager scopeManager, |
|
|
|
SignInManager<ApplicationUser> signInManager, |
|
|
|
UserManager<ApplicationUser> userManager) |
|
|
|
{ |
|
|
|
_applicationManager = applicationManager; |
|
|
|
_authorizationManager = authorizationManager; |
|
|
|
_scopeManager = scopeManager; |
|
|
|
_signInManager = signInManager; |
|
|
|
_userManager = userManager; |
|
|
|
} |
|
|
|
_applicationManager = applicationManager; |
|
|
|
_authorizationManager = authorizationManager; |
|
|
|
_scopeManager = scopeManager; |
|
|
|
_signInManager = signInManager; |
|
|
|
_userManager = userManager; |
|
|
|
} |
|
|
|
|
|
|
|
#region Authorization code, implicit and hybrid flows
|
|
|
|
// Note: to support interactive flows like the code flow,
|
|
|
|
// you must provide your own authorization endpoint action:
|
|
|
|
#region Authorization code, implicit and hybrid flows
|
|
|
|
// Note: to support interactive flows like the code flow,
|
|
|
|
// you must provide your own authorization endpoint action:
|
|
|
|
|
|
|
|
[HttpGet("~/connect/authorize")] |
|
|
|
[HttpPost("~/connect/authorize")] |
|
|
|
[IgnoreAntiforgeryToken] |
|
|
|
public async Task<IActionResult> Authorize() |
|
|
|
[HttpGet("~/connect/authorize")] |
|
|
|
[HttpPost("~/connect/authorize")] |
|
|
|
[IgnoreAntiforgeryToken] |
|
|
|
public async Task<IActionResult> Authorize() |
|
|
|
{ |
|
|
|
var request = HttpContext.GetOpenIddictServerRequest() ?? |
|
|
|
throw new InvalidOperationException("The OpenID Connect request cannot be retrieved."); |
|
|
|
|
|
|
|
// Retrieve the user principal stored in the authentication cookie.
|
|
|
|
// If a max_age parameter was provided, ensure that the cookie is not too old.
|
|
|
|
// If the user principal can't be extracted or the cookie is too old, redirect the user to the login page.
|
|
|
|
var result = await HttpContext.AuthenticateAsync(IdentityConstants.ApplicationScheme); |
|
|
|
if (result == null || !result.Succeeded || (request.MaxAge != null && result.Properties?.IssuedUtc != null && |
|
|
|
DateTimeOffset.UtcNow - result.Properties.IssuedUtc > TimeSpan.FromSeconds(request.MaxAge.Value))) |
|
|
|
{ |
|
|
|
var request = HttpContext.GetOpenIddictServerRequest() ?? |
|
|
|
throw new InvalidOperationException("The OpenID Connect request cannot be retrieved."); |
|
|
|
|
|
|
|
// Retrieve the user principal stored in the authentication cookie.
|
|
|
|
// If a max_age parameter was provided, ensure that the cookie is not too old.
|
|
|
|
// If the user principal can't be extracted or the cookie is too old, redirect the user to the login page.
|
|
|
|
var result = await HttpContext.AuthenticateAsync(IdentityConstants.ApplicationScheme); |
|
|
|
if (result == null || !result.Succeeded || (request.MaxAge != null && result.Properties?.IssuedUtc != null && |
|
|
|
DateTimeOffset.UtcNow - result.Properties.IssuedUtc > TimeSpan.FromSeconds(request.MaxAge.Value))) |
|
|
|
// If the client application requested promptless authentication,
|
|
|
|
// return an error indicating that the user is not logged in.
|
|
|
|
if (request.HasPrompt(Prompts.None)) |
|
|
|
{ |
|
|
|
// If the client application requested promptless authentication,
|
|
|
|
// return an error indicating that the user is not logged in.
|
|
|
|
if (request.HasPrompt(Prompts.None)) |
|
|
|
{ |
|
|
|
return Forbid( |
|
|
|
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, |
|
|
|
properties: new AuthenticationProperties(new Dictionary<string, string> |
|
|
|
{ |
|
|
|
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.LoginRequired, |
|
|
|
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The user is not logged in." |
|
|
|
})); |
|
|
|
} |
|
|
|
|
|
|
|
return Challenge( |
|
|
|
authenticationSchemes: IdentityConstants.ApplicationScheme, |
|
|
|
properties: new AuthenticationProperties |
|
|
|
return Forbid( |
|
|
|
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, |
|
|
|
properties: new AuthenticationProperties(new Dictionary<string, string> |
|
|
|
{ |
|
|
|
RedirectUri = Request.PathBase + Request.Path + QueryString.Create( |
|
|
|
Request.HasFormContentType ? Request.Form.ToList() : Request.Query.ToList()) |
|
|
|
}); |
|
|
|
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.LoginRequired, |
|
|
|
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The user is not logged in." |
|
|
|
})); |
|
|
|
} |
|
|
|
|
|
|
|
// If prompt=login was specified by the client application,
|
|
|
|
// immediately return the user agent to the login page.
|
|
|
|
if (request.HasPrompt(Prompts.Login)) |
|
|
|
{ |
|
|
|
// To avoid endless login -> authorization redirects, the prompt=login flag
|
|
|
|
// is removed from the authorization request payload before redirecting the user.
|
|
|
|
var prompt = string.Join(" ", request.GetPrompts().Remove(Prompts.Login)); |
|
|
|
return Challenge( |
|
|
|
authenticationSchemes: IdentityConstants.ApplicationScheme, |
|
|
|
properties: new AuthenticationProperties |
|
|
|
{ |
|
|
|
RedirectUri = Request.PathBase + Request.Path + QueryString.Create( |
|
|
|
Request.HasFormContentType ? Request.Form.ToList() : Request.Query.ToList()) |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
var parameters = Request.HasFormContentType ? |
|
|
|
Request.Form.Where(parameter => parameter.Key != Parameters.Prompt).ToList() : |
|
|
|
Request.Query.Where(parameter => parameter.Key != Parameters.Prompt).ToList(); |
|
|
|
// If prompt=login was specified by the client application,
|
|
|
|
// immediately return the user agent to the login page.
|
|
|
|
if (request.HasPrompt(Prompts.Login)) |
|
|
|
{ |
|
|
|
// To avoid endless login -> authorization redirects, the prompt=login flag
|
|
|
|
// is removed from the authorization request payload before redirecting the user.
|
|
|
|
var prompt = string.Join(" ", request.GetPrompts().Remove(Prompts.Login)); |
|
|
|
|
|
|
|
parameters.Add(KeyValuePair.Create(Parameters.Prompt, new StringValues(prompt))); |
|
|
|
var parameters = Request.HasFormContentType ? |
|
|
|
Request.Form.Where(parameter => parameter.Key != Parameters.Prompt).ToList() : |
|
|
|
Request.Query.Where(parameter => parameter.Key != Parameters.Prompt).ToList(); |
|
|
|
|
|
|
|
return Challenge( |
|
|
|
authenticationSchemes: IdentityConstants.ApplicationScheme, |
|
|
|
properties: new AuthenticationProperties |
|
|
|
{ |
|
|
|
RedirectUri = Request.PathBase + Request.Path + QueryString.Create(parameters) |
|
|
|
}); |
|
|
|
} |
|
|
|
parameters.Add(KeyValuePair.Create(Parameters.Prompt, new StringValues(prompt))); |
|
|
|
|
|
|
|
return Challenge( |
|
|
|
authenticationSchemes: IdentityConstants.ApplicationScheme, |
|
|
|
properties: new AuthenticationProperties |
|
|
|
{ |
|
|
|
RedirectUri = Request.PathBase + Request.Path + QueryString.Create(parameters) |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
// Retrieve the profile of the logged in user.
|
|
|
|
var user = await _userManager.GetUserAsync(result.Principal) ?? |
|
|
|
throw new InvalidOperationException("The user details cannot be retrieved."); |
|
|
|
// Retrieve the profile of the logged in user.
|
|
|
|
var user = await _userManager.GetUserAsync(result.Principal) ?? |
|
|
|
throw new InvalidOperationException("The user details cannot be retrieved."); |
|
|
|
|
|
|
|
// Retrieve the application details from the database.
|
|
|
|
var application = await _applicationManager.FindByClientIdAsync(request.ClientId) ?? |
|
|
|
throw new InvalidOperationException("Details concerning the calling client application cannot be found."); |
|
|
|
// Retrieve the application details from the database.
|
|
|
|
var application = await _applicationManager.FindByClientIdAsync(request.ClientId) ?? |
|
|
|
throw new InvalidOperationException("Details concerning the calling client application cannot be found."); |
|
|
|
|
|
|
|
// Retrieve the permanent authorizations associated with the user and the calling client application.
|
|
|
|
var authorizations = await _authorizationManager.FindAsync( |
|
|
|
subject: await _userManager.GetUserIdAsync(user), |
|
|
|
client : await _applicationManager.GetIdAsync(application), |
|
|
|
status : Statuses.Valid, |
|
|
|
type : AuthorizationTypes.Permanent, |
|
|
|
scopes : request.GetScopes()).ToListAsync(); |
|
|
|
// Retrieve the permanent authorizations associated with the user and the calling client application.
|
|
|
|
var authorizations = await _authorizationManager.FindAsync( |
|
|
|
subject: await _userManager.GetUserIdAsync(user), |
|
|
|
client : await _applicationManager.GetIdAsync(application), |
|
|
|
status : Statuses.Valid, |
|
|
|
type : AuthorizationTypes.Permanent, |
|
|
|
scopes : request.GetScopes()).ToListAsync(); |
|
|
|
|
|
|
|
switch (await _applicationManager.GetConsentTypeAsync(application)) |
|
|
|
{ |
|
|
|
// If the consent is external (e.g when authorizations are granted by a sysadmin),
|
|
|
|
// immediately return an error if no authorization can be found in the database.
|
|
|
|
case ConsentTypes.External when !authorizations.Any(): |
|
|
|
return Forbid( |
|
|
|
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, |
|
|
|
properties: new AuthenticationProperties(new Dictionary<string, string> |
|
|
|
{ |
|
|
|
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.ConsentRequired, |
|
|
|
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = |
|
|
|
"The logged in user is not allowed to access this client application." |
|
|
|
})); |
|
|
|
|
|
|
|
// If the consent is implicit or if an authorization was found,
|
|
|
|
// return an authorization response without displaying the consent form.
|
|
|
|
case ConsentTypes.Implicit: |
|
|
|
case ConsentTypes.External when authorizations.Any(): |
|
|
|
case ConsentTypes.Explicit when authorizations.Any() && !request.HasPrompt(Prompts.Consent): |
|
|
|
var principal = await _signInManager.CreateUserPrincipalAsync(user); |
|
|
|
|
|
|
|
// Note: in this sample, the granted scopes match the requested scope
|
|
|
|
// but you may want to allow the user to uncheck specific scopes.
|
|
|
|
// For that, simply restrict the list of scopes before calling SetScopes.
|
|
|
|
principal.SetScopes(request.GetScopes()); |
|
|
|
principal.SetResources(await _scopeManager.ListResourcesAsync(principal.GetScopes()).ToListAsync()); |
|
|
|
|
|
|
|
// Automatically create a permanent authorization to avoid requiring explicit consent
|
|
|
|
// for future authorization or token requests containing the same scopes.
|
|
|
|
var authorization = authorizations.LastOrDefault(); |
|
|
|
if (authorization is null) |
|
|
|
switch (await _applicationManager.GetConsentTypeAsync(application)) |
|
|
|
{ |
|
|
|
// If the consent is external (e.g when authorizations are granted by a sysadmin),
|
|
|
|
// immediately return an error if no authorization can be found in the database.
|
|
|
|
case ConsentTypes.External when !authorizations.Any(): |
|
|
|
return Forbid( |
|
|
|
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, |
|
|
|
properties: new AuthenticationProperties(new Dictionary<string, string> |
|
|
|
{ |
|
|
|
authorization = await _authorizationManager.CreateAsync( |
|
|
|
principal: principal, |
|
|
|
subject : await _userManager.GetUserIdAsync(user), |
|
|
|
client : await _applicationManager.GetIdAsync(application), |
|
|
|
type : AuthorizationTypes.Permanent, |
|
|
|
scopes : principal.GetScopes()); |
|
|
|
} |
|
|
|
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.ConsentRequired, |
|
|
|
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = |
|
|
|
"The logged in user is not allowed to access this client application." |
|
|
|
})); |
|
|
|
|
|
|
|
principal.SetAuthorizationId(await _authorizationManager.GetIdAsync(authorization)); |
|
|
|
// If the consent is implicit or if an authorization was found,
|
|
|
|
// return an authorization response without displaying the consent form.
|
|
|
|
case ConsentTypes.Implicit: |
|
|
|
case ConsentTypes.External when authorizations.Any(): |
|
|
|
case ConsentTypes.Explicit when authorizations.Any() && !request.HasPrompt(Prompts.Consent): |
|
|
|
var principal = await _signInManager.CreateUserPrincipalAsync(user); |
|
|
|
|
|
|
|
foreach (var claim in principal.Claims) |
|
|
|
{ |
|
|
|
claim.SetDestinations(GetDestinations(claim, principal)); |
|
|
|
} |
|
|
|
|
|
|
|
return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); |
|
|
|
|
|
|
|
// At this point, no authorization was found in the database and an error must be returned
|
|
|
|
// if the client application specified prompt=none in the authorization request.
|
|
|
|
case ConsentTypes.Explicit when request.HasPrompt(Prompts.None): |
|
|
|
case ConsentTypes.Systematic when request.HasPrompt(Prompts.None): |
|
|
|
return Forbid( |
|
|
|
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, |
|
|
|
properties: new AuthenticationProperties(new Dictionary<string, string> |
|
|
|
{ |
|
|
|
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.ConsentRequired, |
|
|
|
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = |
|
|
|
"Interactive user consent is required." |
|
|
|
})); |
|
|
|
|
|
|
|
// In every other case, render the consent form.
|
|
|
|
default: return View(new AuthorizeViewModel |
|
|
|
// Note: in this sample, the granted scopes match the requested scope
|
|
|
|
// but you may want to allow the user to uncheck specific scopes.
|
|
|
|
// For that, simply restrict the list of scopes before calling SetScopes.
|
|
|
|
principal.SetScopes(request.GetScopes()); |
|
|
|
principal.SetResources(await _scopeManager.ListResourcesAsync(principal.GetScopes()).ToListAsync()); |
|
|
|
|
|
|
|
// Automatically create a permanent authorization to avoid requiring explicit consent
|
|
|
|
// for future authorization or token requests containing the same scopes.
|
|
|
|
var authorization = authorizations.LastOrDefault(); |
|
|
|
if (authorization is null) |
|
|
|
{ |
|
|
|
ApplicationName = await _applicationManager.GetLocalizedDisplayNameAsync(application), |
|
|
|
Scope = request.Scope |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
authorization = await _authorizationManager.CreateAsync( |
|
|
|
principal: principal, |
|
|
|
subject : await _userManager.GetUserIdAsync(user), |
|
|
|
client : await _applicationManager.GetIdAsync(application), |
|
|
|
type : AuthorizationTypes.Permanent, |
|
|
|
scopes : principal.GetScopes()); |
|
|
|
} |
|
|
|
|
|
|
|
[Authorize, FormValueRequired("submit.Accept")] |
|
|
|
[HttpPost("~/connect/authorize"), ValidateAntiForgeryToken] |
|
|
|
public async Task<IActionResult> Accept() |
|
|
|
{ |
|
|
|
var request = HttpContext.GetOpenIddictServerRequest() ?? |
|
|
|
throw new InvalidOperationException("The OpenID Connect request cannot be retrieved."); |
|
|
|
principal.SetAuthorizationId(await _authorizationManager.GetIdAsync(authorization)); |
|
|
|
|
|
|
|
// Retrieve the profile of the logged in user.
|
|
|
|
var user = await _userManager.GetUserAsync(User) ?? |
|
|
|
throw new InvalidOperationException("The user details cannot be retrieved."); |
|
|
|
foreach (var claim in principal.Claims) |
|
|
|
{ |
|
|
|
claim.SetDestinations(GetDestinations(claim, principal)); |
|
|
|
} |
|
|
|
|
|
|
|
// Retrieve the application details from the database.
|
|
|
|
var application = await _applicationManager.FindByClientIdAsync(request.ClientId) ?? |
|
|
|
throw new InvalidOperationException("Details concerning the calling client application cannot be found."); |
|
|
|
return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); |
|
|
|
|
|
|
|
// Retrieve the permanent authorizations associated with the user and the calling client application.
|
|
|
|
var authorizations = await _authorizationManager.FindAsync( |
|
|
|
subject: await _userManager.GetUserIdAsync(user), |
|
|
|
client : await _applicationManager.GetIdAsync(application), |
|
|
|
status : Statuses.Valid, |
|
|
|
type : AuthorizationTypes.Permanent, |
|
|
|
scopes : request.GetScopes()).ToListAsync(); |
|
|
|
|
|
|
|
// Note: the same check is already made in the other action but is repeated
|
|
|
|
// here to ensure a malicious user can't abuse this POST-only endpoint and
|
|
|
|
// force it to return a valid response without the external authorization.
|
|
|
|
if (!authorizations.Any() && await _applicationManager.HasConsentTypeAsync(application, ConsentTypes.External)) |
|
|
|
{ |
|
|
|
// At this point, no authorization was found in the database and an error must be returned
|
|
|
|
// if the client application specified prompt=none in the authorization request.
|
|
|
|
case ConsentTypes.Explicit when request.HasPrompt(Prompts.None): |
|
|
|
case ConsentTypes.Systematic when request.HasPrompt(Prompts.None): |
|
|
|
return Forbid( |
|
|
|
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, |
|
|
|
properties: new AuthenticationProperties(new Dictionary<string, string> |
|
|
|
{ |
|
|
|
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.ConsentRequired, |
|
|
|
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = |
|
|
|
"The logged in user is not allowed to access this client application." |
|
|
|
"Interactive user consent is required." |
|
|
|
})); |
|
|
|
} |
|
|
|
|
|
|
|
var principal = await _signInManager.CreateUserPrincipalAsync(user); |
|
|
|
|
|
|
|
// Note: in this sample, the granted scopes match the requested scope
|
|
|
|
// but you may want to allow the user to uncheck specific scopes.
|
|
|
|
// For that, simply restrict the list of scopes before calling SetScopes.
|
|
|
|
principal.SetScopes(request.GetScopes()); |
|
|
|
principal.SetResources(await _scopeManager.ListResourcesAsync(principal.GetScopes()).ToListAsync()); |
|
|
|
|
|
|
|
// Automatically create a permanent authorization to avoid requiring explicit consent
|
|
|
|
// for future authorization or token requests containing the same scopes.
|
|
|
|
var authorization = authorizations.LastOrDefault(); |
|
|
|
if (authorization is null) |
|
|
|
// In every other case, render the consent form.
|
|
|
|
default: return View(new AuthorizeViewModel |
|
|
|
{ |
|
|
|
authorization = await _authorizationManager.CreateAsync( |
|
|
|
principal: principal, |
|
|
|
subject : await _userManager.GetUserIdAsync(user), |
|
|
|
client : await _applicationManager.GetIdAsync(application), |
|
|
|
type : AuthorizationTypes.Permanent, |
|
|
|
scopes : principal.GetScopes()); |
|
|
|
} |
|
|
|
ApplicationName = await _applicationManager.GetLocalizedDisplayNameAsync(application), |
|
|
|
Scope = request.Scope |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
[Authorize, FormValueRequired("submit.Accept")] |
|
|
|
[HttpPost("~/connect/authorize"), ValidateAntiForgeryToken] |
|
|
|
public async Task<IActionResult> Accept() |
|
|
|
{ |
|
|
|
var request = HttpContext.GetOpenIddictServerRequest() ?? |
|
|
|
throw new InvalidOperationException("The OpenID Connect request cannot be retrieved."); |
|
|
|
|
|
|
|
// Retrieve the profile of the logged in user.
|
|
|
|
var user = await _userManager.GetUserAsync(User) ?? |
|
|
|
throw new InvalidOperationException("The user details cannot be retrieved."); |
|
|
|
|
|
|
|
// Retrieve the application details from the database.
|
|
|
|
var application = await _applicationManager.FindByClientIdAsync(request.ClientId) ?? |
|
|
|
throw new InvalidOperationException("Details concerning the calling client application cannot be found."); |
|
|
|
|
|
|
|
// Retrieve the permanent authorizations associated with the user and the calling client application.
|
|
|
|
var authorizations = await _authorizationManager.FindAsync( |
|
|
|
subject: await _userManager.GetUserIdAsync(user), |
|
|
|
client : await _applicationManager.GetIdAsync(application), |
|
|
|
status : Statuses.Valid, |
|
|
|
type : AuthorizationTypes.Permanent, |
|
|
|
scopes : request.GetScopes()).ToListAsync(); |
|
|
|
|
|
|
|
// Note: the same check is already made in the other action but is repeated
|
|
|
|
// here to ensure a malicious user can't abuse this POST-only endpoint and
|
|
|
|
// force it to return a valid response without the external authorization.
|
|
|
|
if (!authorizations.Any() && await _applicationManager.HasConsentTypeAsync(application, ConsentTypes.External)) |
|
|
|
{ |
|
|
|
return Forbid( |
|
|
|
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, |
|
|
|
properties: new AuthenticationProperties(new Dictionary<string, string> |
|
|
|
{ |
|
|
|
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.ConsentRequired, |
|
|
|
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = |
|
|
|
"The logged in user is not allowed to access this client application." |
|
|
|
})); |
|
|
|
} |
|
|
|
|
|
|
|
principal.SetAuthorizationId(await _authorizationManager.GetIdAsync(authorization)); |
|
|
|
var principal = await _signInManager.CreateUserPrincipalAsync(user); |
|
|
|
|
|
|
|
foreach (var claim in principal.Claims) |
|
|
|
{ |
|
|
|
claim.SetDestinations(GetDestinations(claim, principal)); |
|
|
|
} |
|
|
|
// Note: in this sample, the granted scopes match the requested scope
|
|
|
|
// but you may want to allow the user to uncheck specific scopes.
|
|
|
|
// For that, simply restrict the list of scopes before calling SetScopes.
|
|
|
|
principal.SetScopes(request.GetScopes()); |
|
|
|
principal.SetResources(await _scopeManager.ListResourcesAsync(principal.GetScopes()).ToListAsync()); |
|
|
|
|
|
|
|
// Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens.
|
|
|
|
return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); |
|
|
|
// Automatically create a permanent authorization to avoid requiring explicit consent
|
|
|
|
// for future authorization or token requests containing the same scopes.
|
|
|
|
var authorization = authorizations.LastOrDefault(); |
|
|
|
if (authorization is null) |
|
|
|
{ |
|
|
|
authorization = await _authorizationManager.CreateAsync( |
|
|
|
principal: principal, |
|
|
|
subject : await _userManager.GetUserIdAsync(user), |
|
|
|
client : await _applicationManager.GetIdAsync(application), |
|
|
|
type : AuthorizationTypes.Permanent, |
|
|
|
scopes : principal.GetScopes()); |
|
|
|
} |
|
|
|
|
|
|
|
[Authorize, FormValueRequired("submit.Deny")] |
|
|
|
[HttpPost("~/connect/authorize"), ValidateAntiForgeryToken] |
|
|
|
// Notify OpenIddict that the authorization grant has been denied by the resource owner
|
|
|
|
// to redirect the user agent to the client application using the appropriate response_mode.
|
|
|
|
public IActionResult Deny() => Forbid(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); |
|
|
|
#endregion
|
|
|
|
|
|
|
|
#region Device flow
|
|
|
|
// Note: to support the device flow, you must provide your own verification endpoint action:
|
|
|
|
[Authorize, HttpGet("~/connect/verify")] |
|
|
|
public async Task<IActionResult> Verify() |
|
|
|
principal.SetAuthorizationId(await _authorizationManager.GetIdAsync(authorization)); |
|
|
|
|
|
|
|
foreach (var claim in principal.Claims) |
|
|
|
{ |
|
|
|
var request = HttpContext.GetOpenIddictServerRequest() ?? |
|
|
|
throw new InvalidOperationException("The OpenID Connect request cannot be retrieved."); |
|
|
|
claim.SetDestinations(GetDestinations(claim, principal)); |
|
|
|
} |
|
|
|
|
|
|
|
// If the user code was not specified in the query string (e.g as part of the verification_uri_complete),
|
|
|
|
// render a form to ask the user to enter the user code manually (non-digit chars are automatically ignored).
|
|
|
|
if (string.IsNullOrEmpty(request.UserCode)) |
|
|
|
{ |
|
|
|
return View(new VerifyViewModel()); |
|
|
|
} |
|
|
|
// Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens.
|
|
|
|
return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); |
|
|
|
} |
|
|
|
|
|
|
|
// Retrieve the claims principal associated with the user code.
|
|
|
|
var result = await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); |
|
|
|
if (result.Succeeded) |
|
|
|
{ |
|
|
|
// Retrieve the application details from the database using the client_id stored in the principal.
|
|
|
|
var application = await _applicationManager.FindByClientIdAsync(result.Principal.GetClaim(Claims.ClientId)) ?? |
|
|
|
throw new InvalidOperationException("Details concerning the calling client application cannot be found."); |
|
|
|
[Authorize, FormValueRequired("submit.Deny")] |
|
|
|
[HttpPost("~/connect/authorize"), ValidateAntiForgeryToken] |
|
|
|
// Notify OpenIddict that the authorization grant has been denied by the resource owner
|
|
|
|
// to redirect the user agent to the client application using the appropriate response_mode.
|
|
|
|
public IActionResult Deny() => Forbid(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); |
|
|
|
#endregion
|
|
|
|
|
|
|
|
#region Device flow
|
|
|
|
// Note: to support the device flow, you must provide your own verification endpoint action:
|
|
|
|
[Authorize, HttpGet("~/connect/verify")] |
|
|
|
public async Task<IActionResult> Verify() |
|
|
|
{ |
|
|
|
var request = HttpContext.GetOpenIddictServerRequest() ?? |
|
|
|
throw new InvalidOperationException("The OpenID Connect request cannot be retrieved."); |
|
|
|
|
|
|
|
// Render a form asking the user to confirm the authorization demand.
|
|
|
|
return View(new VerifyViewModel |
|
|
|
{ |
|
|
|
ApplicationName = await _applicationManager.GetLocalizedDisplayNameAsync(application), |
|
|
|
Scope = string.Join(" ", result.Principal.GetScopes()), |
|
|
|
UserCode = request.UserCode |
|
|
|
}); |
|
|
|
} |
|
|
|
// If the user code was not specified in the query string (e.g as part of the verification_uri_complete),
|
|
|
|
// render a form to ask the user to enter the user code manually (non-digit chars are automatically ignored).
|
|
|
|
if (string.IsNullOrEmpty(request.UserCode)) |
|
|
|
{ |
|
|
|
return View(new VerifyViewModel()); |
|
|
|
} |
|
|
|
|
|
|
|
// Redisplay the form when the user code is not valid.
|
|
|
|
// Retrieve the claims principal associated with the user code.
|
|
|
|
var result = await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); |
|
|
|
if (result.Succeeded) |
|
|
|
{ |
|
|
|
// Retrieve the application details from the database using the client_id stored in the principal.
|
|
|
|
var application = await _applicationManager.FindByClientIdAsync(result.Principal.GetClaim(Claims.ClientId)) ?? |
|
|
|
throw new InvalidOperationException("Details concerning the calling client application cannot be found."); |
|
|
|
|
|
|
|
// Render a form asking the user to confirm the authorization demand.
|
|
|
|
return View(new VerifyViewModel |
|
|
|
{ |
|
|
|
Error = Errors.InvalidToken, |
|
|
|
ErrorDescription = "The specified user code is not valid. Please make sure you typed it correctly." |
|
|
|
ApplicationName = await _applicationManager.GetLocalizedDisplayNameAsync(application), |
|
|
|
Scope = string.Join(" ", result.Principal.GetScopes()), |
|
|
|
UserCode = request.UserCode |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
[Authorize, FormValueRequired("submit.Accept")] |
|
|
|
[HttpPost("~/connect/verify"), ValidateAntiForgeryToken] |
|
|
|
public async Task<IActionResult> VerifyAccept() |
|
|
|
// Redisplay the form when the user code is not valid.
|
|
|
|
return View(new VerifyViewModel |
|
|
|
{ |
|
|
|
// Retrieve the profile of the logged in user.
|
|
|
|
var user = await _userManager.GetUserAsync(User) ?? |
|
|
|
throw new InvalidOperationException("The user details cannot be retrieved."); |
|
|
|
|
|
|
|
// Retrieve the claims principal associated with the user code.
|
|
|
|
var result = await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); |
|
|
|
if (result.Succeeded) |
|
|
|
{ |
|
|
|
var principal = await _signInManager.CreateUserPrincipalAsync(user); |
|
|
|
|
|
|
|
// Note: in this sample, the granted scopes match the requested scope
|
|
|
|
// but you may want to allow the user to uncheck specific scopes.
|
|
|
|
// For that, simply restrict the list of scopes before calling SetScopes.
|
|
|
|
principal.SetScopes(result.Principal.GetScopes()); |
|
|
|
principal.SetResources(await _scopeManager.ListResourcesAsync(principal.GetScopes()).ToListAsync()); |
|
|
|
Error = Errors.InvalidToken, |
|
|
|
ErrorDescription = "The specified user code is not valid. Please make sure you typed it correctly." |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
foreach (var claim in principal.Claims) |
|
|
|
{ |
|
|
|
claim.SetDestinations(GetDestinations(claim, principal)); |
|
|
|
} |
|
|
|
[Authorize, FormValueRequired("submit.Accept")] |
|
|
|
[HttpPost("~/connect/verify"), ValidateAntiForgeryToken] |
|
|
|
public async Task<IActionResult> VerifyAccept() |
|
|
|
{ |
|
|
|
// Retrieve the profile of the logged in user.
|
|
|
|
var user = await _userManager.GetUserAsync(User) ?? |
|
|
|
throw new InvalidOperationException("The user details cannot be retrieved."); |
|
|
|
|
|
|
|
var properties = new AuthenticationProperties |
|
|
|
{ |
|
|
|
// This property points to the address OpenIddict will automatically
|
|
|
|
// redirect the user to after validating the authorization demand.
|
|
|
|
RedirectUri = "/" |
|
|
|
}; |
|
|
|
// Retrieve the claims principal associated with the user code.
|
|
|
|
var result = await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); |
|
|
|
if (result.Succeeded) |
|
|
|
{ |
|
|
|
var principal = await _signInManager.CreateUserPrincipalAsync(user); |
|
|
|
|
|
|
|
return SignIn(principal, properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); |
|
|
|
} |
|
|
|
// Note: in this sample, the granted scopes match the requested scope
|
|
|
|
// but you may want to allow the user to uncheck specific scopes.
|
|
|
|
// For that, simply restrict the list of scopes before calling SetScopes.
|
|
|
|
principal.SetScopes(result.Principal.GetScopes()); |
|
|
|
principal.SetResources(await _scopeManager.ListResourcesAsync(principal.GetScopes()).ToListAsync()); |
|
|
|
|
|
|
|
// Redisplay the form when the user code is not valid.
|
|
|
|
return View(new VerifyViewModel |
|
|
|
foreach (var claim in principal.Claims) |
|
|
|
{ |
|
|
|
Error = Errors.InvalidToken, |
|
|
|
ErrorDescription = "The specified user code is not valid. Please make sure you typed it correctly." |
|
|
|
}); |
|
|
|
} |
|
|
|
claim.SetDestinations(GetDestinations(claim, principal)); |
|
|
|
} |
|
|
|
|
|
|
|
[Authorize, FormValueRequired("submit.Deny")] |
|
|
|
[HttpPost("~/connect/verify"), ValidateAntiForgeryToken] |
|
|
|
// Notify OpenIddict that the authorization grant has been denied by the resource owner.
|
|
|
|
public IActionResult VerifyDeny() => Forbid( |
|
|
|
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, |
|
|
|
properties: new AuthenticationProperties() |
|
|
|
var properties = new AuthenticationProperties |
|
|
|
{ |
|
|
|
// This property points to the address OpenIddict will automatically
|
|
|
|
// redirect the user to after rejecting the authorization demand.
|
|
|
|
// redirect the user to after validating the authorization demand.
|
|
|
|
RedirectUri = "/" |
|
|
|
}); |
|
|
|
#endregion
|
|
|
|
|
|
|
|
#region Logout support for interactive flows like code and implicit
|
|
|
|
// Note: the logout action is only useful when implementing interactive
|
|
|
|
// flows like the authorization code flow or the implicit flow.
|
|
|
|
|
|
|
|
[HttpGet("~/connect/logout")] |
|
|
|
public IActionResult Logout() => View(); |
|
|
|
}; |
|
|
|
|
|
|
|
[ActionName(nameof(Logout)), HttpPost("~/connect/logout"), ValidateAntiForgeryToken] |
|
|
|
public async Task<IActionResult> LogoutPost() |
|
|
|
{ |
|
|
|
// Ask ASP.NET Core Identity to delete the local and external cookies created
|
|
|
|
// when the user agent is redirected from the external identity provider
|
|
|
|
// after a successful authentication flow (e.g Google or Facebook).
|
|
|
|
await _signInManager.SignOutAsync(); |
|
|
|
|
|
|
|
// Returning a SignOutResult will ask OpenIddict to redirect the user agent
|
|
|
|
// to the post_logout_redirect_uri specified by the client application or to
|
|
|
|
// the RedirectUri specified in the authentication properties if none was set.
|
|
|
|
return SignOut( |
|
|
|
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, |
|
|
|
properties: new AuthenticationProperties |
|
|
|
{ |
|
|
|
RedirectUri = "/" |
|
|
|
}); |
|
|
|
return SignIn(principal, properties, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); |
|
|
|
} |
|
|
|
#endregion
|
|
|
|
|
|
|
|
#region Password, authorization code, device and refresh token flows
|
|
|
|
// Note: to support non-interactive flows like password,
|
|
|
|
// you must provide your own token endpoint action:
|
|
|
|
// Redisplay the form when the user code is not valid.
|
|
|
|
return View(new VerifyViewModel |
|
|
|
{ |
|
|
|
Error = Errors.InvalidToken, |
|
|
|
ErrorDescription = "The specified user code is not valid. Please make sure you typed it correctly." |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
[HttpPost("~/connect/token"), Produces("application/json")] |
|
|
|
public async Task<IActionResult> Exchange() |
|
|
|
[Authorize, FormValueRequired("submit.Deny")] |
|
|
|
[HttpPost("~/connect/verify"), ValidateAntiForgeryToken] |
|
|
|
// Notify OpenIddict that the authorization grant has been denied by the resource owner.
|
|
|
|
public IActionResult VerifyDeny() => Forbid( |
|
|
|
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, |
|
|
|
properties: new AuthenticationProperties() |
|
|
|
{ |
|
|
|
var request = HttpContext.GetOpenIddictServerRequest() ?? |
|
|
|
throw new InvalidOperationException("The OpenID Connect request cannot be retrieved."); |
|
|
|
// This property points to the address OpenIddict will automatically
|
|
|
|
// redirect the user to after rejecting the authorization demand.
|
|
|
|
RedirectUri = "/" |
|
|
|
}); |
|
|
|
#endregion
|
|
|
|
|
|
|
|
if (request.IsPasswordGrantType()) |
|
|
|
{ |
|
|
|
var user = await _userManager.FindByNameAsync(request.Username); |
|
|
|
if (user is null) |
|
|
|
{ |
|
|
|
return Forbid( |
|
|
|
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, |
|
|
|
properties: new AuthenticationProperties(new Dictionary<string, string> |
|
|
|
{ |
|
|
|
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant, |
|
|
|
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The username/password couple is invalid." |
|
|
|
})); |
|
|
|
} |
|
|
|
#region Logout support for interactive flows like code and implicit
|
|
|
|
// Note: the logout action is only useful when implementing interactive
|
|
|
|
// flows like the authorization code flow or the implicit flow.
|
|
|
|
|
|
|
|
// Validate the username/password parameters and ensure the account is not locked out.
|
|
|
|
var result = await _signInManager.CheckPasswordSignInAsync(user, request.Password, lockoutOnFailure: true); |
|
|
|
if (!result.Succeeded) |
|
|
|
{ |
|
|
|
return Forbid( |
|
|
|
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, |
|
|
|
properties: new AuthenticationProperties(new Dictionary<string, string> |
|
|
|
{ |
|
|
|
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant, |
|
|
|
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The username/password couple is invalid." |
|
|
|
})); |
|
|
|
} |
|
|
|
[HttpGet("~/connect/logout")] |
|
|
|
public IActionResult Logout() => View(); |
|
|
|
|
|
|
|
var principal = await _signInManager.CreateUserPrincipalAsync(user); |
|
|
|
[ActionName(nameof(Logout)), HttpPost("~/connect/logout"), ValidateAntiForgeryToken] |
|
|
|
public async Task<IActionResult> LogoutPost() |
|
|
|
{ |
|
|
|
// Ask ASP.NET Core Identity to delete the local and external cookies created
|
|
|
|
// when the user agent is redirected from the external identity provider
|
|
|
|
// after a successful authentication flow (e.g Google or Facebook).
|
|
|
|
await _signInManager.SignOutAsync(); |
|
|
|
|
|
|
|
// Returning a SignOutResult will ask OpenIddict to redirect the user agent
|
|
|
|
// to the post_logout_redirect_uri specified by the client application or to
|
|
|
|
// the RedirectUri specified in the authentication properties if none was set.
|
|
|
|
return SignOut( |
|
|
|
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, |
|
|
|
properties: new AuthenticationProperties |
|
|
|
{ |
|
|
|
RedirectUri = "/" |
|
|
|
}); |
|
|
|
} |
|
|
|
#endregion
|
|
|
|
|
|
|
|
// Note: in this sample, the granted scopes match the requested scope
|
|
|
|
// but you may want to allow the user to uncheck specific scopes.
|
|
|
|
// For that, simply restrict the list of scopes before calling SetScopes.
|
|
|
|
principal.SetScopes(request.GetScopes()); |
|
|
|
principal.SetResources(await _scopeManager.ListResourcesAsync(principal.GetScopes()).ToListAsync()); |
|
|
|
#region Password, authorization code, device and refresh token flows
|
|
|
|
// Note: to support non-interactive flows like password,
|
|
|
|
// you must provide your own token endpoint action:
|
|
|
|
|
|
|
|
foreach (var claim in principal.Claims) |
|
|
|
{ |
|
|
|
claim.SetDestinations(GetDestinations(claim, principal)); |
|
|
|
} |
|
|
|
[HttpPost("~/connect/token"), Produces("application/json")] |
|
|
|
public async Task<IActionResult> Exchange() |
|
|
|
{ |
|
|
|
var request = HttpContext.GetOpenIddictServerRequest() ?? |
|
|
|
throw new InvalidOperationException("The OpenID Connect request cannot be retrieved."); |
|
|
|
|
|
|
|
// Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens.
|
|
|
|
return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); |
|
|
|
if (request.IsPasswordGrantType()) |
|
|
|
{ |
|
|
|
var user = await _userManager.FindByNameAsync(request.Username); |
|
|
|
if (user is null) |
|
|
|
{ |
|
|
|
return Forbid( |
|
|
|
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, |
|
|
|
properties: new AuthenticationProperties(new Dictionary<string, string> |
|
|
|
{ |
|
|
|
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant, |
|
|
|
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The username/password couple is invalid." |
|
|
|
})); |
|
|
|
} |
|
|
|
|
|
|
|
else if (request.IsAuthorizationCodeGrantType() || request.IsDeviceCodeGrantType() || request.IsRefreshTokenGrantType()) |
|
|
|
// Validate the username/password parameters and ensure the account is not locked out.
|
|
|
|
var result = await _signInManager.CheckPasswordSignInAsync(user, request.Password, lockoutOnFailure: true); |
|
|
|
if (!result.Succeeded) |
|
|
|
{ |
|
|
|
// Retrieve the claims principal stored in the authorization code/device code/refresh token.
|
|
|
|
var principal = (await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)).Principal; |
|
|
|
|
|
|
|
// Retrieve the user profile corresponding to the authorization code/refresh token.
|
|
|
|
// Note: if you want to automatically invalidate the authorization code/refresh token
|
|
|
|
// when the user password/roles change, use the following line instead:
|
|
|
|
// var user = _signInManager.ValidateSecurityStampAsync(info.Principal);
|
|
|
|
var user = await _userManager.GetUserAsync(principal); |
|
|
|
if (user is null) |
|
|
|
{ |
|
|
|
return Forbid( |
|
|
|
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, |
|
|
|
properties: new AuthenticationProperties(new Dictionary<string, string> |
|
|
|
{ |
|
|
|
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant, |
|
|
|
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The token is no longer valid." |
|
|
|
})); |
|
|
|
} |
|
|
|
return Forbid( |
|
|
|
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, |
|
|
|
properties: new AuthenticationProperties(new Dictionary<string, string> |
|
|
|
{ |
|
|
|
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant, |
|
|
|
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The username/password couple is invalid." |
|
|
|
})); |
|
|
|
} |
|
|
|
|
|
|
|
// Ensure the user is still allowed to sign in.
|
|
|
|
if (!await _signInManager.CanSignInAsync(user)) |
|
|
|
{ |
|
|
|
return Forbid( |
|
|
|
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, |
|
|
|
properties: new AuthenticationProperties(new Dictionary<string, string> |
|
|
|
{ |
|
|
|
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant, |
|
|
|
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The user is no longer allowed to sign in." |
|
|
|
})); |
|
|
|
} |
|
|
|
var principal = await _signInManager.CreateUserPrincipalAsync(user); |
|
|
|
|
|
|
|
foreach (var claim in principal.Claims) |
|
|
|
{ |
|
|
|
claim.SetDestinations(GetDestinations(claim, principal)); |
|
|
|
} |
|
|
|
// Note: in this sample, the granted scopes match the requested scope
|
|
|
|
// but you may want to allow the user to uncheck specific scopes.
|
|
|
|
// For that, simply restrict the list of scopes before calling SetScopes.
|
|
|
|
principal.SetScopes(request.GetScopes()); |
|
|
|
principal.SetResources(await _scopeManager.ListResourcesAsync(principal.GetScopes()).ToListAsync()); |
|
|
|
|
|
|
|
// Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens.
|
|
|
|
return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); |
|
|
|
foreach (var claim in principal.Claims) |
|
|
|
{ |
|
|
|
claim.SetDestinations(GetDestinations(claim, principal)); |
|
|
|
} |
|
|
|
|
|
|
|
throw new InvalidOperationException("The specified grant type is not supported."); |
|
|
|
// Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens.
|
|
|
|
return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); |
|
|
|
} |
|
|
|
#endregion
|
|
|
|
|
|
|
|
private IEnumerable<string> GetDestinations(Claim claim, ClaimsPrincipal principal) |
|
|
|
else if (request.IsAuthorizationCodeGrantType() || request.IsDeviceCodeGrantType() || request.IsRefreshTokenGrantType()) |
|
|
|
{ |
|
|
|
// Note: by default, claims are NOT automatically included in the access and identity tokens.
|
|
|
|
// To allow OpenIddict to serialize them, you must attach them a destination, that specifies
|
|
|
|
// whether they should be included in access tokens, in identity tokens or in both.
|
|
|
|
// Retrieve the claims principal stored in the authorization code/device code/refresh token.
|
|
|
|
var principal = (await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme)).Principal; |
|
|
|
|
|
|
|
// Retrieve the user profile corresponding to the authorization code/refresh token.
|
|
|
|
// Note: if you want to automatically invalidate the authorization code/refresh token
|
|
|
|
// when the user password/roles change, use the following line instead:
|
|
|
|
// var user = _signInManager.ValidateSecurityStampAsync(info.Principal);
|
|
|
|
var user = await _userManager.GetUserAsync(principal); |
|
|
|
if (user is null) |
|
|
|
{ |
|
|
|
return Forbid( |
|
|
|
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, |
|
|
|
properties: new AuthenticationProperties(new Dictionary<string, string> |
|
|
|
{ |
|
|
|
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant, |
|
|
|
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The token is no longer valid." |
|
|
|
})); |
|
|
|
} |
|
|
|
|
|
|
|
switch (claim.Type) |
|
|
|
// Ensure the user is still allowed to sign in.
|
|
|
|
if (!await _signInManager.CanSignInAsync(user)) |
|
|
|
{ |
|
|
|
case Claims.Name: |
|
|
|
yield return Destinations.AccessToken; |
|
|
|
return Forbid( |
|
|
|
authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, |
|
|
|
properties: new AuthenticationProperties(new Dictionary<string, string> |
|
|
|
{ |
|
|
|
[OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant, |
|
|
|
[OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The user is no longer allowed to sign in." |
|
|
|
})); |
|
|
|
} |
|
|
|
|
|
|
|
if (principal.HasScope(Scopes.Profile)) |
|
|
|
yield return Destinations.IdentityToken; |
|
|
|
foreach (var claim in principal.Claims) |
|
|
|
{ |
|
|
|
claim.SetDestinations(GetDestinations(claim, principal)); |
|
|
|
} |
|
|
|
|
|
|
|
yield break; |
|
|
|
// Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens.
|
|
|
|
return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); |
|
|
|
} |
|
|
|
|
|
|
|
case Claims.Email: |
|
|
|
yield return Destinations.AccessToken; |
|
|
|
throw new InvalidOperationException("The specified grant type is not supported."); |
|
|
|
} |
|
|
|
#endregion
|
|
|
|
|
|
|
|
if (principal.HasScope(Scopes.Email)) |
|
|
|
yield return Destinations.IdentityToken; |
|
|
|
private IEnumerable<string> GetDestinations(Claim claim, ClaimsPrincipal principal) |
|
|
|
{ |
|
|
|
// Note: by default, claims are NOT automatically included in the access and identity tokens.
|
|
|
|
// To allow OpenIddict to serialize them, you must attach them a destination, that specifies
|
|
|
|
// whether they should be included in access tokens, in identity tokens or in both.
|
|
|
|
|
|
|
|
yield break; |
|
|
|
switch (claim.Type) |
|
|
|
{ |
|
|
|
case Claims.Name: |
|
|
|
yield return Destinations.AccessToken; |
|
|
|
|
|
|
|
case Claims.Role: |
|
|
|
yield return Destinations.AccessToken; |
|
|
|
if (principal.HasScope(Scopes.Profile)) |
|
|
|
yield return Destinations.IdentityToken; |
|
|
|
|
|
|
|
if (principal.HasScope(Scopes.Roles)) |
|
|
|
yield return Destinations.IdentityToken; |
|
|
|
yield break; |
|
|
|
|
|
|
|
yield break; |
|
|
|
case Claims.Email: |
|
|
|
yield return Destinations.AccessToken; |
|
|
|
|
|
|
|
// Never include the security stamp in the access and identity tokens, as it's a secret value.
|
|
|
|
case "AspNet.Identity.SecurityStamp": yield break; |
|
|
|
if (principal.HasScope(Scopes.Email)) |
|
|
|
yield return Destinations.IdentityToken; |
|
|
|
|
|
|
|
default: |
|
|
|
yield return Destinations.AccessToken; |
|
|
|
yield break; |
|
|
|
} |
|
|
|
yield break; |
|
|
|
|
|
|
|
case Claims.Role: |
|
|
|
yield return Destinations.AccessToken; |
|
|
|
|
|
|
|
if (principal.HasScope(Scopes.Roles)) |
|
|
|
yield return Destinations.IdentityToken; |
|
|
|
|
|
|
|
yield break; |
|
|
|
|
|
|
|
// Never include the security stamp in the access and identity tokens, as it's a secret value.
|
|
|
|
case "AspNet.Identity.SecurityStamp": yield break; |
|
|
|
|
|
|
|
default: |
|
|
|
yield return Destinations.AccessToken; |
|
|
|
yield break; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|