27 changed files with 176 additions and 963 deletions
@ -1,192 +0,0 @@ |
|||||
/* |
|
||||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
|
||||
* See https://github.com/openiddict/openiddict-core for more information concerning
|
|
||||
* the license and the contributors participating to this project. |
|
||||
*/ |
|
||||
|
|
||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.Linq; |
|
||||
using System.Security.Claims; |
|
||||
using System.Threading; |
|
||||
using System.Threading.Tasks; |
|
||||
using AspNet.Security.OpenIdConnect.Extensions; |
|
||||
using AspNet.Security.OpenIdConnect.Server; |
|
||||
using JetBrains.Annotations; |
|
||||
using Microsoft.AspNetCore.Builder; |
|
||||
using Microsoft.AspNetCore.Http; |
|
||||
using Microsoft.AspNetCore.Identity; |
|
||||
using Microsoft.Extensions.DependencyInjection; |
|
||||
using Microsoft.Extensions.Logging; |
|
||||
using Microsoft.Extensions.Options; |
|
||||
|
|
||||
namespace OpenIddict { |
|
||||
/// <summary>
|
|
||||
/// Provides methods allowing to manage the users stored in the store.
|
|
||||
/// </summary>
|
|
||||
/// <typeparam name="TUser">The type of the User entity.</typeparam>
|
|
||||
public class OpenIddictUserManager<TUser> : UserManager<TUser> where TUser : class { |
|
||||
public OpenIddictUserManager( |
|
||||
[NotNull] IServiceProvider services, |
|
||||
[NotNull] IOpenIddictUserStore<TUser> store, |
|
||||
[NotNull] IOptions<IdentityOptions> options, |
|
||||
[NotNull] ILogger<OpenIddictUserManager<TUser>> logger, |
|
||||
[NotNull] IPasswordHasher<TUser> hasher, |
|
||||
[NotNull] IEnumerable<IUserValidator<TUser>> userValidators, |
|
||||
[NotNull] IEnumerable<IPasswordValidator<TUser>> passwordValidators, |
|
||||
[NotNull] ILookupNormalizer keyNormalizer, |
|
||||
[NotNull] IdentityErrorDescriber errors) |
|
||||
: base(store, options, hasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger) { |
|
||||
Context = services?.GetRequiredService<IHttpContextAccessor>()?.HttpContext; |
|
||||
Logger = logger; |
|
||||
Options = options.Value; |
|
||||
Store = store; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the cancellation token used to abort async operations.
|
|
||||
/// </summary>
|
|
||||
protected CancellationToken CancellationToken => Context?.RequestAborted ?? CancellationToken.None; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the HTTP context associated with the current manager.
|
|
||||
/// </summary>
|
|
||||
protected HttpContext Context { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the identity options.
|
|
||||
/// </summary>
|
|
||||
protected IdentityOptions Options { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the store associated with the current manager.
|
|
||||
/// </summary>
|
|
||||
protected new IOpenIddictUserStore<TUser> Store { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates a new <see cref="ClaimsIdentity"/> used to create new tokens.
|
|
||||
/// </summary>
|
|
||||
/// <param name="user">The user corresponding to the identity.</param>
|
|
||||
/// <param name="scopes">The scopes granted by the resource owner.</param>
|
|
||||
/// <returns>
|
|
||||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|
||||
/// whose result returns the <see cref="ClaimsIdentity"/> corresponding to the user.
|
|
||||
/// </returns>
|
|
||||
public virtual async Task<ClaimsIdentity> CreateIdentityAsync(TUser user, IEnumerable<string> scopes) { |
|
||||
if (user == null) { |
|
||||
throw new ArgumentNullException(nameof(user)); |
|
||||
} |
|
||||
|
|
||||
if (scopes == null) { |
|
||||
throw new ArgumentNullException(nameof(scopes)); |
|
||||
} |
|
||||
|
|
||||
var identity = new ClaimsIdentity( |
|
||||
OpenIdConnectServerDefaults.AuthenticationScheme, |
|
||||
Options.ClaimsIdentity.UserNameClaimType, |
|
||||
Options.ClaimsIdentity.RoleClaimType); |
|
||||
|
|
||||
// Note: the name identifier is always included in both identity and
|
|
||||
// access tokens, even if an explicit destination is not specified.
|
|
||||
identity.AddClaim(ClaimTypes.NameIdentifier, await GetUserIdAsync(user)); |
|
||||
|
|
||||
// Only add the name claim if the "profile" scope was granted.
|
|
||||
if (scopes.Contains(OpenIdConnectConstants.Scopes.Profile)) { |
|
||||
var username = await GetUserNameAsync(user); |
|
||||
|
|
||||
identity.AddClaim(ClaimTypes.Name, username, |
|
||||
OpenIdConnectConstants.Destinations.AccessToken, |
|
||||
OpenIdConnectConstants.Destinations.IdentityToken); |
|
||||
} |
|
||||
|
|
||||
// Only add the email address if the "email" scope was granted.
|
|
||||
if (SupportsUserEmail && scopes.Contains(OpenIdConnectConstants.Scopes.Email)) { |
|
||||
var email = await GetEmailAsync(user); |
|
||||
|
|
||||
identity.AddClaim(ClaimTypes.Email, email, |
|
||||
OpenIdConnectConstants.Destinations.AccessToken, |
|
||||
OpenIdConnectConstants.Destinations.IdentityToken); |
|
||||
} |
|
||||
|
|
||||
if (SupportsUserRole && scopes.Contains(OpenIddictConstants.Scopes.Roles)) { |
|
||||
foreach (var role in await GetRolesAsync(user)) { |
|
||||
identity.AddClaim(identity.RoleClaimType, role, |
|
||||
OpenIdConnectConstants.Destinations.AccessToken, |
|
||||
OpenIdConnectConstants.Destinations.IdentityToken); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
if (SupportsUserSecurityStamp) { |
|
||||
var stamp = await GetSecurityStampAsync(user); |
|
||||
|
|
||||
if (!string.IsNullOrEmpty(stamp)) { |
|
||||
identity.AddClaim(Options.ClaimsIdentity.SecurityStampClaimType, stamp, |
|
||||
OpenIdConnectConstants.Destinations.AccessToken, |
|
||||
OpenIdConnectConstants.Destinations.IdentityToken); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return identity; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates a new token associated with the given user.
|
|
||||
/// </summary>
|
|
||||
/// <param name="user">The user.</param>
|
|
||||
/// <param name="type">The token type.</param>
|
|
||||
/// <returns>
|
|
||||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|
||||
/// whose result returns the unique identifier associated with the token.
|
|
||||
/// </returns>
|
|
||||
public virtual Task<string> CreateTokenAsync(TUser user, string type) { |
|
||||
if (user == null) { |
|
||||
throw new ArgumentNullException(nameof(user)); |
|
||||
} |
|
||||
|
|
||||
if (string.IsNullOrEmpty(type)) { |
|
||||
throw new ArgumentException("The token type cannot be null or empty.", nameof(type)); |
|
||||
} |
|
||||
|
|
||||
return Store.CreateTokenAsync(user, type, CancellationToken); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates a new token associated with the given user and
|
|
||||
/// attached to the tokens issued to the specified client.
|
|
||||
/// </summary>
|
|
||||
/// <param name="user">The user.</param>
|
|
||||
/// <param name="client">The application.</param>
|
|
||||
/// <param name="type">The token type.</param>
|
|
||||
/// <returns>
|
|
||||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|
||||
/// whose result returns the unique identifier associated with the token.
|
|
||||
/// </returns>
|
|
||||
public virtual Task<string> CreateTokenAsync(TUser user, string client, string type) { |
|
||||
if (user == null) { |
|
||||
throw new ArgumentNullException(nameof(user)); |
|
||||
} |
|
||||
|
|
||||
if (string.IsNullOrEmpty(type)) { |
|
||||
throw new ArgumentException("The token type cannot be null or empty.", nameof(type)); |
|
||||
} |
|
||||
|
|
||||
return Store.CreateTokenAsync(user, client, type, CancellationToken); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Retrieves the token identifiers associated with a user.
|
|
||||
/// </summary>
|
|
||||
/// <param name="user">The user.</param>
|
|
||||
/// <returns>
|
|
||||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|
||||
/// whose result returns the tokens associated with the user.
|
|
||||
/// </returns>
|
|
||||
public virtual Task<IEnumerable<string>> GetTokensAsync(TUser user) { |
|
||||
if (user == null) { |
|
||||
throw new ArgumentNullException(nameof(user)); |
|
||||
} |
|
||||
|
|
||||
return Store.GetTokensAsync(user, CancellationToken); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,55 +0,0 @@ |
|||||
/* |
|
||||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
|
||||
* See https://github.com/openiddict/openiddict-core for more information concerning
|
|
||||
* the license and the contributors participating to this project. |
|
||||
*/ |
|
||||
|
|
||||
using System.Collections.Generic; |
|
||||
using System.Threading; |
|
||||
using System.Threading.Tasks; |
|
||||
using Microsoft.AspNetCore.Identity; |
|
||||
|
|
||||
namespace OpenIddict { |
|
||||
/// <summary>
|
|
||||
/// Provides methods allowing to manage the users stored in a database.
|
|
||||
/// </summary>
|
|
||||
/// <typeparam name="TUser">The type of the User entity.</typeparam>
|
|
||||
public interface IOpenIddictUserStore<TUser> : IUserStore<TUser> where TUser : class { |
|
||||
/// <summary>
|
|
||||
/// Creates a new token associated with the given user.
|
|
||||
/// </summary>
|
|
||||
/// <param name="user">The user associated with the token.</param>
|
|
||||
/// <param name="type">The token type.</param>
|
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|
||||
/// <returns>
|
|
||||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|
||||
/// whose result returns the unique identifier associated with the token.
|
|
||||
/// </returns>
|
|
||||
Task<string> CreateTokenAsync(TUser user, string type, CancellationToken cancellationToken); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates a new token associated with the given user and
|
|
||||
/// attached to the tokens issued to the specified client.
|
|
||||
/// </summary>
|
|
||||
/// <param name="user">The user.</param>
|
|
||||
/// <param name="client">The application.</param>
|
|
||||
/// <param name="type">The token type.</param>
|
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|
||||
/// <returns>
|
|
||||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|
||||
/// whose result returns the unique identifier associated with the token.
|
|
||||
/// </returns>
|
|
||||
Task<string> CreateTokenAsync(TUser user, string client, string type, CancellationToken cancellationToken); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Retrieves the token identifiers associated with a user.
|
|
||||
/// </summary>
|
|
||||
/// <param name="user">The user.</param>
|
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|
||||
/// <returns>
|
|
||||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|
||||
/// whose result returns the tokens associated with the user.
|
|
||||
/// </returns>
|
|
||||
Task<IEnumerable<string>> GetTokensAsync(TUser user, CancellationToken cancellationToken); |
|
||||
} |
|
||||
} |
|
||||
@ -1,162 +0,0 @@ |
|||||
/* |
|
||||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
|
||||
* See https://github.com/openiddict/openiddict-core for more information concerning
|
|
||||
* the license and the contributors participating to this project. |
|
||||
*/ |
|
||||
|
|
||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using System.ComponentModel; |
|
||||
using System.Linq; |
|
||||
using System.Threading; |
|
||||
using System.Threading.Tasks; |
|
||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore; |
|
||||
using Microsoft.EntityFrameworkCore; |
|
||||
|
|
||||
namespace OpenIddict { |
|
||||
/// <summary>
|
|
||||
/// Provides methods allowing to manage the users stored in a database.
|
|
||||
/// </summary>
|
|
||||
/// <typeparam name="TUser">The type of the User entity.</typeparam>
|
|
||||
/// <typeparam name="TApplication">The type of the Application entity.</typeparam>
|
|
||||
/// <typeparam name="TAuthorization">The type of the Authorization entity.</typeparam>
|
|
||||
/// <typeparam name="TRole">The type of the Role entity.</typeparam>
|
|
||||
/// <typeparam name="TToken">The type of the Token entity.</typeparam>
|
|
||||
/// <typeparam name="TContext">The type of the Entity Framework database context.</typeparam>
|
|
||||
/// <typeparam name="TKey">The type of the entity primary keys.</typeparam>
|
|
||||
public class OpenIddictUserStore<TUser, TApplication, TAuthorization, TRole, TToken, TContext, TKey> : |
|
||||
UserStore<TUser, TRole, TContext, TKey>, IOpenIddictUserStore<TUser> |
|
||||
where TUser : OpenIddictUser<TKey, TAuthorization, TToken>, new() |
|
||||
where TApplication : OpenIddictApplication<TKey, TToken> |
|
||||
where TAuthorization : OpenIddictAuthorization<TKey, TToken> |
|
||||
where TRole : IdentityRole<TKey> |
|
||||
where TToken : OpenIddictToken<TKey>, new() |
|
||||
where TContext : DbContext |
|
||||
where TKey : IEquatable<TKey> { |
|
||||
public OpenIddictUserStore(TContext context) |
|
||||
: base(context) { } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the database set corresponding to the <typeparamref name="TApplication"/> entity.
|
|
||||
/// </summary>
|
|
||||
protected DbSet<TApplication> Applications => Context.Set<TApplication>(); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates a new token associated with the given user and defined by a unique identifier and a token type.
|
|
||||
/// </summary>
|
|
||||
/// <param name="user">The user associated with the token.</param>
|
|
||||
/// <param name="type">The token type.</param>
|
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|
||||
/// <returns>
|
|
||||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|
||||
/// whose result returns the unique identifier associated with the token.
|
|
||||
/// </returns>
|
|
||||
public virtual async Task<string> CreateTokenAsync(TUser user, string type, CancellationToken cancellationToken) { |
|
||||
if (user == null) { |
|
||||
throw new ArgumentNullException(nameof(user)); |
|
||||
} |
|
||||
|
|
||||
if (string.IsNullOrEmpty(type)) { |
|
||||
throw new ArgumentException("The token type cannot be null or empty."); |
|
||||
} |
|
||||
|
|
||||
// Ensure that the key type can be serialized.
|
|
||||
var converter = TypeDescriptor.GetConverter(typeof(TKey)); |
|
||||
if (!converter.CanConvertTo(typeof(string))) { |
|
||||
throw new InvalidOperationException($"The '{typeof(TKey).Name}' key type is not supported."); |
|
||||
} |
|
||||
|
|
||||
var token = new TToken { Type = type }; |
|
||||
user.Tokens.Add(token); |
|
||||
|
|
||||
Context.Update(user); |
|
||||
|
|
||||
await Context.SaveChangesAsync(cancellationToken); |
|
||||
|
|
||||
return converter.ConvertToInvariantString(token.Id); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Creates a new token associated with the given user and
|
|
||||
/// attached to the tokens issued to the specified client.
|
|
||||
/// </summary>
|
|
||||
/// <param name="user">The user.</param>
|
|
||||
/// <param name="client">The application.</param>
|
|
||||
/// <param name="type">The token type.</param>
|
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|
||||
/// <returns>
|
|
||||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|
||||
/// whose result returns the unique identifier associated with the token.
|
|
||||
/// </returns>
|
|
||||
public virtual async Task<string> CreateTokenAsync(TUser user, string client, string type, CancellationToken cancellationToken) { |
|
||||
if (user == null) { |
|
||||
throw new ArgumentNullException(nameof(user)); |
|
||||
} |
|
||||
|
|
||||
if (string.IsNullOrEmpty(client)) { |
|
||||
throw new ArgumentException("The client identifier cannot be null or empty."); |
|
||||
} |
|
||||
|
|
||||
if (string.IsNullOrEmpty(type)) { |
|
||||
throw new ArgumentException("The token type cannot be null or empty."); |
|
||||
} |
|
||||
|
|
||||
// Ensure that the key type can be serialized.
|
|
||||
var converter = TypeDescriptor.GetConverter(typeof(TKey)); |
|
||||
if (!converter.CanConvertTo(typeof(string))) { |
|
||||
throw new InvalidOperationException($"The '{typeof(TKey).Name}' key type is not supported."); |
|
||||
} |
|
||||
|
|
||||
var application = await Applications.FirstOrDefaultAsync(entity => entity.ClientId.Equals(client), cancellationToken); |
|
||||
if (application == null) { |
|
||||
throw new InvalidOperationException("The application cannot be found in the database."); |
|
||||
} |
|
||||
|
|
||||
var token = new TToken { Type = type }; |
|
||||
|
|
||||
application.Tokens.Add(token); |
|
||||
user.Tokens.Add(token); |
|
||||
|
|
||||
Context.Update(application); |
|
||||
Context.Update(user); |
|
||||
|
|
||||
await Context.SaveChangesAsync(cancellationToken); |
|
||||
|
|
||||
return converter.ConvertToInvariantString(token.Id); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Retrieves the token identifiers associated with a user.
|
|
||||
/// </summary>
|
|
||||
/// <param name="user">The user.</param>
|
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> that can be used to abort the operation.</param>
|
|
||||
/// <returns>
|
|
||||
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
|
|
||||
/// whose result returns the tokens associated with the user.
|
|
||||
/// </returns>
|
|
||||
public virtual async Task<IEnumerable<string>> GetTokensAsync(TUser user, CancellationToken cancellationToken) { |
|
||||
if (user == null) { |
|
||||
throw new ArgumentNullException(nameof(user)); |
|
||||
} |
|
||||
|
|
||||
// Ensure that the key type can be serialized.
|
|
||||
var converter = TypeDescriptor.GetConverter(typeof(TKey)); |
|
||||
if (!converter.CanConvertTo(typeof(string))) { |
|
||||
throw new InvalidOperationException($"The '{typeof(TKey).Name}' key type is not supported."); |
|
||||
} |
|
||||
|
|
||||
var query = from entity in Users |
|
||||
where entity.Id.Equals(user.Id) |
|
||||
from token in entity.Tokens |
|
||||
select token.Id; |
|
||||
|
|
||||
var tokens = new List<string>(); |
|
||||
|
|
||||
foreach (var identifier in await query.ToArrayAsync()) { |
|
||||
tokens.Add(converter.ConvertToInvariantString(identifier)); |
|
||||
} |
|
||||
|
|
||||
return tokens; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
Loading…
Reference in new issue