I have 2 problems really, 1 is that i do not want to use efcore, as i am currently using ef6.1, so at the moment i have 2 databases, which isnt great.
Second problem is that i dont want to use EF Identity, as i want to create my own Account table and store what i want.
Is it possible for me to do this with openIddict? In a previous question, i believe you said you were thinking of dropping the need for Identity??
In a previous question, i believe you said you were thinking of dropping the need for Identity??
That's right. As of beta1, OpenIddict.Core no longer depends on ASP.NET Core Identity. To use OI without Identity or EF Core, you'll have to implement the IOpenIddictApplicationStore and IOpenIddictTokenStore stores.
@PinpointTownes when will beta1 be available? I think this is a great direction to go down!
Beta1 is already available
And is there any documentation stating exactly how to implement this yet? As far as implementing the interfaces, DI etc
@Gillardo @ogix is right, the first beta1 package was pushed to MyGet yesterday.
Concerning the documentation, no such thing yet. That said, it should be really trivial. You can take a look at the existing EF Core stores to see how they are registered.
@PinpointTownes am i right in thinking that you have to create my own Authorization and Scope objects as well, as well as implement Stores for them? I have done Application and Token, but when i come to calling the AddOpenIddict extension, it is expecting values for Authorization and Scope. There is no overload to just pass in Application and Token...?
@PinpointTownes also in the samples folder, you are still tied to using Identity, as you can see in the startup.cs file
// Register the Identity services.
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
and here
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
namespace Mvc.Server.Models {
// Add profile data for application users by adding properties to the ApplicationUser class
public class ApplicationUser : IdentityUser { }
}
am i right in thinking that you have to create my own Authorization and Scope objects as well, as well as implement Stores for them?
These models/stores correspond to WIP things. They are mainly here as a placeholder (i.e they are not used yet). You could probably use object for the generic parameters.
@PinpointTownes also in the samples folder, you are still tied to using Identity, as you can see in the startup.cs file
Of course. ASP.NET Core Identity is still the most popular membership stack so it doesn't seem unreasonable to use it in the samples :trollface:
@PinpointTownes dont worry about all this, i have managed to not use EFCore and also use my own custom identity objects in EF6.1, by implementing the IOpenIddictApplicationStore and IOpenIddictTokenStore.
Then on the startup.cs file i had to change AddIdentity to this
services.AddIdentity<MY_CUSTOM_USER, MY_CUSTOM_ROLE>(config =>
{
// if we are accessing the /api and an unauthorized request is made
// do not redirect to the login page, but simply return "Unauthorized"
config.Cookies.ApplicationCookie.Events = new CookieAuthenticationEvents
{
OnRedirectToLogin = ctx =>
{
if (ctx.Request.Path.StartsWithSegments("/api"))
ctx.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
return System.Threading.Tasks.Task.FromResult(0);
}
};
});
After this you need to create your own stores implementing the interfaces IUserStore, IUserPasswordStore and IRoleStore
After doing this, it seems to mostly work, my only problem is that my token seems to be valid, and the User.Identity.IsAuthenticated flag is set to true but the User.Identity.Name property seems to be null?
Any ideas why this would be? I am not using cookies in my system, just the tokens.
I have attached some sample code below. I am using EF6.1 (using interface for my DbContext)
public class MyCustomUserStore : IUserStore<Account>, IUserPasswordStore<Account>
{
#region IUserStore
public async Task<IdentityResult> CreateAsync(Account user, CancellationToken cancellationToken)
{
if (user == null)
{
var error = new IdentityError { Code = "1", Description = "User account cannot be null" };
return IdentityResult.Failed(error);
}
_context.SetAdded(user);
await _context.SaveChangesAsync();
return IdentityResult.Success;
}
public async Task<IdentityResult> DeleteAsync(Account user, CancellationToken cancellationToken)
{
if (user == null)
{
var error = new IdentityError { Code = "1", Description = "User account cannot be null" };
return IdentityResult.Failed(error);
}
_context.SetDeleted(user);
await _context.SaveChangesAsync();
return IdentityResult.Success;
}
public void Dispose()
{
//throw new NotImplementedException();
}
public Task<Account> FindByIdAsync(string userId, CancellationToken cancellationToken)
{
return _context.Accounts
.Where(e => e.Id.ToString() == userId)
.FirstOrDefaultAsync();
}
public Task<Account> FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken)
{
return _context.Accounts
.Where(e => e.Username.ToUpper().Equals(normalizedUserName.ToUpper()))
.FirstOrDefaultAsync();
}
public Task<string> GetNormalizedUserNameAsync(Account user, CancellationToken cancellationToken)
{
return Task.FromResult(user.Username.ToUpper());
}
public Task<string> GetUserIdAsync(Account user, CancellationToken cancellationToken)
{
return Task.FromResult(user.Id.ToString());
}
public Task<string> GetUserNameAsync(Account user, CancellationToken cancellationToken)
{
return Task.FromResult(user.Username);
}
public Task SetNormalizedUserNameAsync(Account user, string normalizedName, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task SetUserNameAsync(Account user, string userName, CancellationToken cancellationToken)
{
return Task.Run(() => user.Username = userName);
}
public async Task<IdentityResult> UpdateAsync(Account user, CancellationToken cancellationToken)
{
if (user == null)
{
var error = new IdentityError { Code = "1", Description = "User account cannot be null" };
return IdentityResult.Failed(error);
}
_context.SetModified(user);
await _context.SaveChangesAsync();
return IdentityResult.Success;
}
#endregion
#region IUserPasswordStore
public Task SetPasswordHashAsync(Account user, string passwordHash, CancellationToken cancellationToken)
{
user.PasswordHash = passwordHash;
return Task.FromResult(0);
}
public Task<string> GetPasswordHashAsync(Account user, CancellationToken cancellationToken)
{
return Task.FromResult(user.PasswordHash);
}
public Task<bool> HasPasswordAsync(Account user, CancellationToken cancellationToken)
{
return Task.FromResult(!string.IsNullOrEmpty(user.PasswordHash));
}
#endregion
private IDataContext _context;
public OpenIdAccountStore(IDataContext context)
{
_context = context;
}
}
public class MyCustomApplicationStore : IOpenIddictApplicationStore<Application>
{
#region IOpenIddictApplicationStore
/// <summary>
/// Creates a new application.
/// </summary>
/// <param name="application"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<string> CreateAsync(Application application, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
_context.Applications.Add(application);
await _context.SaveChangesAsync();
return application.Id.ToString();
}
/// <summary>
/// Retrieves an application using its client identifier.
/// </summary>
/// <param name="identifier"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public Task<Application> FindByClientIdAsync(string identifier, CancellationToken cancellationToken)
{
return _context.Applications
.Where(e => e.ClientId == identifier)
.FirstOrDefaultAsync();
}
/// <summary>
/// Retrieves an application using its unique identifier.
/// </summary>
/// <param name="identifier"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public Task<Application> FindByIdAsync(string identifier, CancellationToken cancellationToken)
{
return _context.Applications
.Where(e => e.Id.ToString() == identifier)
.FirstOrDefaultAsync();
}
/// <summary>
/// Retrieves an application using its post_logout_redirect_uri.
/// </summary>
/// <param name="url"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public Task<Application> FindByLogoutRedirectUri(string url, CancellationToken cancellationToken)
{
return _context.Applications
.Where(e => e.LogoutRedirectUri == url)
.FirstOrDefaultAsync();
}
/// <summary>
/// Retrieves the client type associated with an application.
/// </summary>
/// <param name="application"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public Task<string> GetClientTypeAsync(Application application, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
return Task.FromResult(application.Type);
}
/// <summary>
/// Retrieves the display name associated with an application.
/// </summary>
/// <param name="application"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public Task<string> GetDisplayNameAsync(Application application, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
return Task.FromResult(application.Name);
}
/// <summary>
/// Retrieves the hashed secret associated with an application.
/// </summary>
/// <param name="application"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public Task<string> GetHashedSecretAsync(Application application, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
return Task.FromResult(application.ClientSecret);
}
/// <summary>
/// Retrieves the callback address associated with an application.
/// </summary>
/// <param name="application"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public Task<string> GetRedirectUriAsync(Application application, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
return Task.FromResult(application.RedirectUri);
}
/// <summary>
/// Retrieves the token identifiers associated with an application.
/// </summary>
/// <param name="application"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<IEnumerable<string>> GetTokensAsync(Application application, CancellationToken cancellationToken)
{
if (application == null)
{
throw new ArgumentNullException(nameof(application));
}
var tokens = await _context.Applications
.Where(e => e.Id == application.Id)
.SelectMany(e => e.Tokens)
.Select(e => e.Id.ToString())
.ToListAsync();
return tokens;
}
#endregion
private IDataContext _context;
public OpenIdApplicationStore(IDataContext context)
{
_context = context;
}
}
public class MyCustomRoleStore : IRoleStore<Role>
{
#region IRoleStore
public async Task<IdentityResult> CreateAsync(Role role, CancellationToken cancellationToken)
{
if (role == null)
{
var error = new IdentityError { Code = "1", Description = "Role cannot be null" };
return IdentityResult.Failed(error);
}
_context.SetAdded(role);
await _context.SaveChangesAsync();
return IdentityResult.Success;
}
public async Task<IdentityResult> DeleteAsync(Role role, CancellationToken cancellationToken)
{
if (role == null)
{
var error = new IdentityError { Code = "1", Description = "Role cannot be null" };
return IdentityResult.Failed(error);
}
_context.SetDeleted(role);
await _context.SaveChangesAsync();
return IdentityResult.Success;
}
public void Dispose()
{
//throw new NotImplementedException();
}
public Task<Role> FindByIdAsync(string roleId, CancellationToken cancellationToken)
{
return _context.Roles
.Where(e => e.Id.ToString().Equals(roleId))
.FirstOrDefaultAsync();
}
public Task<Role> FindByNameAsync(string normalizedRoleName, CancellationToken cancellationToken)
{
return _context.Roles
.Where(e => e.Name.ToUpper().Equals(normalizedRoleName.ToUpper()))
.FirstOrDefaultAsync();
}
public Task<string> GetNormalizedRoleNameAsync(Role role, CancellationToken cancellationToken)
{
return Task.FromResult(role.Name.ToUpper());
}
public Task<string> GetRoleIdAsync(Role role, CancellationToken cancellationToken)
{
return Task.FromResult(role.Id.ToString());
}
public Task<string> GetRoleNameAsync(Role role, CancellationToken cancellationToken)
{
return Task.FromResult(role.Name);
}
public Task SetNormalizedRoleNameAsync(Role role, string normalizedName, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task SetRoleNameAsync(Role role, string roleName, CancellationToken cancellationToken)
{
return Task.Run(() => role.Name = roleName);
}
public Task<IdentityResult> UpdateAsync(Role role, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
#endregion
private IDataContext _context;
public OpenIdRoleStore(IDataContext context)
{
_context = context;
}
}
public class MyCustomTokenStore : IOpenIddictTokenStore<Token>
{
#region IOpenIddictTokenStore
/// <summary>
/// Creates a new token, which is not associated with a particular user or client.
/// </summary>
/// <param name="type"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async Task<string> CreateAsync(string type, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(type))
{
throw new ArgumentException("The token type cannot be null or empty.");
}
var token = new Token { Type = type };
_context.Tokens.Add(token);
await _context.SaveChangesAsync();
return token.Id.ToString();
}
/// <summary>
/// Retrieves an token using its unique identifier.
/// </summary>
/// <param name="identifier"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public Task<Token> FindByIdAsync(string identifier, CancellationToken cancellationToken)
{
return _context.Tokens
.Where(e => e.Id.ToString() == identifier)
.FirstOrDefaultAsync();
}
/// <summary>
/// Revokes a token.
/// </summary>
/// <param name="token"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public Task RevokeAsync(Token token, CancellationToken cancellationToken)
{
if (token == null)
{
throw new ArgumentNullException(nameof(token));
}
_context.Tokens.Remove(token);
return _context.SaveChangesAsync();
}
#endregion
private IDataContext _context;
public OpenIdTokenStore(IDataContext context)
{
_context = context;
}
}
public class AuthorizationController : Controller
{
private readonly OpenIddictApplicationManager<Application> _applicationManager;
private readonly SignInManager<Account> _signInManager;
private readonly UserManager<Account> _userManager;
private readonly AppSettings _appSettings;
public AuthorizationController(
OpenIddictApplicationManager<Application> applicationManager,
SignInManager<Account> signInManager,
UserManager<Account> userManager,
AppSettings appSettings)
{
_applicationManager = applicationManager;
_signInManager = signInManager;
_userManager = userManager;
_appSettings = appSettings;
}
// Note: to support interactive flows like the code flow,
// you must provide your own authorization endpoint action:
[Authorize, HttpGet, Route("~/connect/authorize")]
public async Task<IActionResult> Authorize(OpenIdConnectRequest request)
{
// Retrieve the application details from the database.
var application = await _applicationManager.FindByClientIdAsync(request.ClientId);
if (application == null)
{
return View("Error", new ErrorViewModel
{
Error = OpenIdConnectConstants.Errors.InvalidClient,
ErrorDescription = "Details concerning the calling client application cannot be found in the database"
});
}
// Flow the request_id to allow OpenIddict to restore
// the original authorization request from the cache.
return View(new AuthorizeViewModel
{
ApplicationName = application.Name,
RequestId = request.RequestId,
Scope = request.Scope
});
}
[Authorize, HttpPost("~/connect/authorize/accept"), ValidateAntiForgeryToken]
public async Task<IActionResult> Accept(OpenIdConnectRequest request)
{
// Retrieve the profile of the logged in user.
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return View("Error", new ErrorViewModel
{
Error = OpenIdConnectConstants.Errors.ServerError,
ErrorDescription = "An internal error has occurred"
});
}
// Create a new authentication ticket.
var ticket = await CreateTicketAsync(request, user);
// Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens.
return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);
}
[Authorize, HttpPost("~/connect/authorize/deny"), ValidateAntiForgeryToken]
public IActionResult Deny()
{
// 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.
return Forbid(OpenIdConnectServerDefaults.AuthenticationScheme);
}
[HttpGet("~/connect/logout")]
public IActionResult Logout(OpenIdConnectRequest request)
{
// Flow the request_id to allow OpenIddict to restore
// the original logout request from the distributed cache.
return View(new LogoutViewModel
{
RequestId = request.RequestId
});
}
[HttpPost("~/connect/logout"), ValidateAntiForgeryToken]
public async Task<IActionResult> Logout()
{
// 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.
return SignOut(OpenIdConnectServerDefaults.AuthenticationScheme);
}
// Note: to support non-interactive flows like password,
// you must provide your own token endpoint action:
[HttpPost("~/connect/token")]
[Produces("application/json")]
public async Task<IActionResult> Exchange(OpenIdConnectRequest request)
{
if (request.IsPasswordGrantType())
{
var user = await _userManager.FindByNameAsync(request.Username);
if (user == null)
{
return BadRequest(new OpenIdConnectResponse
{
Error = OpenIdConnectConstants.Errors.InvalidGrant,
ErrorDescription = "The username/password couple is invalid."
});
}
// Ensure the user is allowed to sign in.
if (!await _signInManager.CanSignInAsync(user))
{
return BadRequest(new OpenIdConnectResponse
{
Error = OpenIdConnectConstants.Errors.InvalidGrant,
ErrorDescription = "The specified user is not allowed to sign in."
});
}
// Reject the token request if two-factor authentication has been enabled by the user.
if (_userManager.SupportsUserTwoFactor && await _userManager.GetTwoFactorEnabledAsync(user))
{
return BadRequest(new OpenIdConnectResponse
{
Error = OpenIdConnectConstants.Errors.InvalidGrant,
ErrorDescription = "The specified user is not allowed to sign in."
});
}
// Ensure the user is not already locked out.
if (_userManager.SupportsUserLockout && await _userManager.IsLockedOutAsync(user))
{
return BadRequest(new OpenIdConnectResponse
{
Error = OpenIdConnectConstants.Errors.InvalidGrant,
ErrorDescription = "The username/password couple is invalid."
});
}
// Ensure the password is valid.
if (!await _userManager.CheckPasswordAsync(user, request.Password))
{
if (_userManager.SupportsUserLockout)
{
await _userManager.AccessFailedAsync(user);
}
return BadRequest(new OpenIdConnectResponse
{
Error = OpenIdConnectConstants.Errors.InvalidGrant,
ErrorDescription = "The username/password couple is invalid."
});
}
if (_userManager.SupportsUserLockout)
{
await _userManager.ResetAccessFailedCountAsync(user);
}
// Create a new authentication ticket.
var ticket = await CreateTicketAsync(request, user);
return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);
}
return BadRequest(new OpenIdConnectResponse
{
Error = OpenIdConnectConstants.Errors.UnsupportedGrantType,
ErrorDescription = "The specified grant type is not supported."
});
}
private async Task<AuthenticationTicket> CreateTicketAsync(OpenIdConnectRequest request, Account user)
{
// Set the list of scopes granted to the client application.
// Note: the offline_access scope must be granted
// to allow OpenIddict to return a refresh token.
var scopes = new[] {
OpenIdConnectConstants.Scopes.OpenId,
OpenIdConnectConstants.Scopes.Email,
OpenIdConnectConstants.Scopes.Profile,
OpenIdConnectConstants.Scopes.OfflineAccess,
OpenIddictConstants.Scopes.Roles
}.Intersect(request.GetScopes());
// Create a new ClaimsPrincipal containing the claims that
// will be used to create an id_token, a token or a code.
var principal = await _signInManager.CreateUserPrincipalAsync(user);
// 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.
foreach (var claim in principal.Claims)
{
// Always include the user identifier in the
// access token and the identity token.
if (claim.Type == ClaimTypes.NameIdentifier)
{
claim.SetDestinations(OpenIdConnectConstants.Destinations.AccessToken,
OpenIdConnectConstants.Destinations.IdentityToken);
}
// Include the name claim, but only if the "profile" scope was requested.
else if (claim.Type == ClaimTypes.Name && scopes.Contains(OpenIdConnectConstants.Scopes.Profile))
{
claim.SetDestinations(OpenIdConnectConstants.Destinations.IdentityToken);
}
// Include the role claims, but only if the "roles" scope was requested.
else if (claim.Type == ClaimTypes.Role && scopes.Contains(OpenIddictConstants.Scopes.Roles))
{
claim.SetDestinations(OpenIdConnectConstants.Destinations.AccessToken,
OpenIdConnectConstants.Destinations.IdentityToken);
}
// The other claims won't be added to the access
// and identity tokens and will be kept private.
}
// Create a new authentication ticket holding the user identity.
var ticket = new AuthenticationTicket(
principal, new AuthenticationProperties(),
OpenIdConnectServerDefaults.AuthenticationScheme);
// Set resource
ticket.SetResources(new string[] { _appSettings.Url.ToLower() });
// Set scopes
ticket.SetScopes(scopes);
return ticket;
}
}
CHANGES TO STARTUP.CS
public void ConfigureServices(IServiceCollection services)
{
// identity
services.AddIdentity<Account, Role>(config =>
{
// if we are accessing the /api and an unauthorized request is made
// do not redirect to the login page, but simply return "Unauthorized"
config.Cookies.ApplicationCookie.Events = new CookieAuthenticationEvents
{
OnRedirectToLogin = ctx =>
{
if (ctx.Request.Path.StartsWithSegments("/api"))
ctx.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
return System.Threading.Tasks.Task.FromResult(0);
}
};
});
// Register the OpenIddict services, custom stores are registered manually
services.AddOpenIddict<Application, Authorization, Scope, Token>()
// Register the ASP.NET Core MVC binder used by OpenIddict.
// Note: if you don't call this method, you won't be able to
// bind OpenIdConnectRequest or OpenIdConnectResponse parameters.
.AddMvcBinders()
// Enable the authorization, logout, token and userinfo endpoints.
.EnableAuthorizationEndpoint("/connect/authorize")
.EnableLogoutEndpoint("/connect/logout")
.EnableTokenEndpoint("/connect/token")
.EnableUserinfoEndpoint("/connect/userinfo")
// Note: the Mvc.Client sample only uses the code flow and the password flow, but you
// can enable the other flows if you need to support implicit or client credentials.
.AllowAuthorizationCodeFlow()
.AllowPasswordFlow()
.AllowRefreshTokenFlow()
// Make the "client_id" parameter mandatory when sending a token request.
//.RequireClientIdentification()
// During development, you can disable the HTTPS requirement.
.DisableHttpsRequirement()
// Use JWT
.UseJsonWebTokens()
// Register a new ephemeral key, that is discarded when the application
// shuts down. Tokens signed using this key are automatically invalidated.
// This method should only be used during development.
.AddEphemeralSigningKey();
// OpenIddict / Identity
services.AddScoped<Microsoft.AspNetCore.Identity.IRoleStore<Role>, MyCustomRoleStore>();
services.AddScoped<Microsoft.AspNetCore.Identity.IUserStore<Account>, MyCustomAccountStore>();
services.AddScoped<IOpenIddictApplicationStore<Application>, MyCustomApplicationStore>();
services.AddScoped<IOpenIddictTokenStore<Token>, MyCustomTokenStore>();
}
So you can see what i mean...

After doing this, it seems to mostly work, my only problem is that my token seems to be valid, and the User.Identity.IsAuthenticated flag is set to true but the User.Identity.Name property seems to be null?
That's because the CreateTicketAsync sample doesn't attach the "access_token" destination to the ClaimTypes.Name claim.
I'll probably simplify CreateTicketAsync so it includes all the claims in both the access and identity tokens and add a big warning comment on top of that :smile:
@PinpointTownes your a god!! I copied the code from the sample, so wonder if it should be updated as well, so it works as it did before??
I changed the code to this in the CreateTicketAsync method. Seems to work perfectly now.
foreach (var claim in principal.Claims)
{
// Always include the user identifier in the
// access token and the identity token.
if (claim.Type == ClaimTypes.NameIdentifier)
{
claim.SetDestinations(OpenIdConnectConstants.Destinations.AccessToken,
OpenIdConnectConstants.Destinations.IdentityToken);
}
// Include the name claim, but only if the "profile" scope was requested.
else if (claim.Type == ClaimTypes.Name && scopes.Contains(OpenIdConnectConstants.Scopes.Profile))
{
claim.SetDestinations(OpenIdConnectConstants.Destinations.AccessToken,
OpenIdConnectConstants.Destinations.IdentityToken);
}
// Include the role claims, but only if the "roles" scope was requested.
else if (claim.Type == ClaimTypes.Role && scopes.Contains(OpenIddictConstants.Scopes.Roles))
{
claim.SetDestinations(OpenIdConnectConstants.Destinations.AccessToken,
OpenIdConnectConstants.Destinations.IdentityToken);
}
// The other claims won't be added to the access
// and identity tokens and will be kept private.
}
Btw, this was an excellent move removing the need for efcore and allowing your own custom objects!! thank you again
Btw, this was an excellent move removing the need for efcore and allowing your own custom objects!! thank you again
Note that it was already possible to create your own non-EF Core stores with the alpha2 bits. The recent changes consisted in removing the direct dependency on ASP.NET Core Identity.
But glad you like the changes :smile:
@Gillardo do you have a repository of what you have done there?
If I want to use my already existing User: Entity model, I cannot do a User: IdentityUser<int> . This throws a runtime error
"App.Data.Models.User', on 'Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore`4[TUser,TRole,TContext,TKey]' violates the constraint of type 'TUser'.
Do you know the solution?
@geocine I haven't...but I can upload it somewhere if people want. Mine works beautifully!!
@Gillardo I want it 馃憤
OK...I will download the sample server application from openiddict and change it to use my code...Probably won't be tonight though...but will do it as soon as I can
Do you know the solution?
It would probably help if you shared your Startup config :smile:
Yeah that's fine...will all be included in sample from openiddict
@Gillardo @PinpointTownes no worries. I was able to figure it out using your examples :)
@Gillardo I am interested in your sample on how you implemented custom tables in oppeniddict.
Could you possibly upload it?
@jpflick I can create a sample for you, if you require with all basic code. I am using EF6, so you may have to adept.
Will upload shortly for you
@jpflick I have created a simple project with all the bits you need. It doesnt run as i am sorry but strapped for time, but you should be able to figure it all out from here. I have created all my custom objects with a prefix of My, so you can tell what i have done.
Hope this helps
@Gillardo I am trying to implement a custom store (IOpenIddictApplicationStore) for RavenDb and I am stuck at IDataContext from your sample code. Is there a working example you can share.
From the OpenIddict Mongo Db module it seems I need to build a entire dbcontext for RavenDb. Can you shed some light on how the DbContext for a custom store is created. Thanks.
@ganu81 what exactly are u stuck on? IDataContext is just an interface I use for DI
Most helpful comment
@PinpointTownes dont worry about all this, i have managed to not use EFCore and also use my own custom identity objects in EF6.1, by implementing the
IOpenIddictApplicationStoreandIOpenIddictTokenStore.Then on the startup.cs file i had to change AddIdentity to this
After this you need to create your own stores implementing the interfaces
IUserStore,IUserPasswordStoreandIRoleStoreAfter doing this, it seems to mostly work, my only problem is that my token seems to be valid, and the
User.Identity.IsAuthenticatedflag is set to true but theUser.Identity.Nameproperty seems to be null?Any ideas why this would be? I am not using cookies in my system, just the tokens.
I have attached some sample code below. I am using EF6.1 (using interface for my DbContext)
CHANGES TO STARTUP.CS