Browse Source

Introduce OpenIddictApplication.ClientId to separate the public client identifier from the internal primary key

pull/140/head
XperiAndri 10 years ago
committed by Kévin Chalet
parent
commit
4a3b84ddb5
  1. 2
      samples/Mvc.Client/Startup.cs
  2. 6
      samples/Mvc.Server/Models/ApplicationDbContext.cs
  3. 5
      samples/Mvc.Server/Models/ApplicationUser.cs
  4. 19
      samples/Mvc.Server/Startup.cs
  5. 2
      src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Authentication.cs
  6. 4
      src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Exchange.cs
  7. 2
      src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Introspection.cs
  8. 2
      src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Revocation.cs
  9. 2
      src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Serialization.cs
  10. 12
      src/OpenIddict.Core/Managers/OpenIddictApplicationManager.cs
  11. 11
      src/OpenIddict.Core/Stores/IOpenIddictApplicationStore.cs
  12. 18
      src/OpenIddict.EntityFramework/Models/OpenIddictApplication.cs
  13. 3
      src/OpenIddict.EntityFramework/OpenIddictContext.cs
  14. 15
      src/OpenIddict.EntityFramework/Stores/OpenIddictApplicationStore.cs
  15. 6
      src/OpenIddict.EntityFramework/Stores/OpenIddictUserStore.cs
  16. 4
      src/OpenIddict.Mvc/OpenIddictController.cs

2
samples/Mvc.Client/Startup.cs

@ -46,7 +46,7 @@ namespace Mvc.Client {
// the different endpoints URIs or the token validation parameters explicitly. // the different endpoints URIs or the token validation parameters explicitly.
Authority = "http://localhost:54540/", Authority = "http://localhost:54540/",
Scope = { "email", "roles" } Scope = { "email", "roles", "offline_access" }
}); });
app.UseMvc(); app.UseMvc();

6
samples/Mvc.Server/Models/ApplicationDbContext.cs

@ -1,8 +1,10 @@
using Microsoft.EntityFrameworkCore; using System;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using OpenIddict; using OpenIddict;
namespace Mvc.Server.Models { namespace Mvc.Server.Models {
public class ApplicationDbContext : OpenIddictContext<ApplicationUser> { public class ApplicationDbContext : OpenIddictContext<ApplicationUser, IdentityRole<Guid>, Guid> {
public ApplicationDbContext(DbContextOptions options) public ApplicationDbContext(DbContextOptions options)
: base(options) { } : base(options) { }

5
samples/Mvc.Server/Models/ApplicationUser.cs

@ -1,6 +1,7 @@
using OpenIddict; using System;
using OpenIddict;
namespace Mvc.Server.Models { namespace Mvc.Server.Models {
// Add profile data for application users by adding properties to the ApplicationUser class // Add profile data for application users by adding properties to the ApplicationUser class
public class ApplicationUser : OpenIddictUser { } public class ApplicationUser : OpenIddictUser<Guid> { }
} }

19
samples/Mvc.Server/Startup.cs

@ -1,3 +1,4 @@
using System;
using System.Linq; using System.Linq;
using AspNet.Security.OAuth.GitHub; using AspNet.Security.OAuth.GitHub;
using CryptoHelper; using CryptoHelper;
@ -28,12 +29,12 @@ namespace Mvc.Server {
options.UseSqlServer(configuration["Data:DefaultConnection:ConnectionString"])); options.UseSqlServer(configuration["Data:DefaultConnection:ConnectionString"]));
// Register the Identity services. // Register the Identity services.
services.AddIdentity<ApplicationUser, IdentityRole>() services.AddIdentity<ApplicationUser, IdentityRole<Guid>>()
.AddEntityFrameworkStores<ApplicationDbContext>() .AddEntityFrameworkStores<ApplicationDbContext, Guid>()
.AddDefaultTokenProviders(); .AddDefaultTokenProviders();
// Register the OpenIddict services, including the default Entity Framework stores. // Register the OpenIddict services, including the default Entity Framework stores.
services.AddOpenIddict<ApplicationUser, ApplicationDbContext>() services.AddOpenIddict<ApplicationUser, IdentityRole<Guid>, ApplicationDbContext, Guid>()
// Register the HTML/CSS assets and MVC modules to handle the interactive flows. // Register the HTML/CSS assets and MVC modules to handle the interactive flows.
// Note: these modules are not necessary when using your own authorization controller // Note: these modules are not necessary when using your own authorization controller
// or when using non-interactive flows-only like the resource owner password credentials grant. // or when using non-interactive flows-only like the resource owner password credentials grant.
@ -146,12 +147,12 @@ namespace Mvc.Server {
// Type = OpenIddictConstants.ClientTypes.Confidential // Type = OpenIddictConstants.ClientTypes.Confidential
// }); // });
context.Applications.Add(new OpenIddictApplication { context.Applications.Add(new OpenIddictApplication<Guid> {
Id = "myClient", ClientId = "myClient",
ClientSecret = Crypto.HashPassword("secret_secret_secret"),
DisplayName = "My client application", DisplayName = "My client application",
RedirectUri = "http://localhost:53507/signin-oidc",
LogoutRedirectUri = "http://localhost:53507/", LogoutRedirectUri = "http://localhost:53507/",
Secret = Crypto.HashPassword("secret_secret_secret"), RedirectUri = "http://localhost:53507/signin-oidc",
Type = OpenIddictConstants.ClientTypes.Confidential Type = OpenIddictConstants.ClientTypes.Confidential
}); });
@ -164,8 +165,8 @@ namespace Mvc.Server {
// * Scope: openid email profile roles // * Scope: openid email profile roles
// * Grant type: authorization code // * Grant type: authorization code
// * Request access token locally: yes // * Request access token locally: yes
context.Applications.Add(new OpenIddictApplication { context.Applications.Add(new OpenIddictApplication<Guid> {
Id = "postman", ClientId = "postman",
DisplayName = "Postman", DisplayName = "Postman",
RedirectUri = "https://www.getpostman.com/oauth2/callback", RedirectUri = "https://www.getpostman.com/oauth2/callback",
Type = OpenIddictConstants.ClientTypes.Public Type = OpenIddictConstants.ClientTypes.Public

2
src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Authentication.cs

@ -36,7 +36,7 @@ namespace OpenIddict.Infrastructure {
} }
// Retrieve the application details corresponding to the requested client_id. // Retrieve the application details corresponding to the requested client_id.
var application = await services.Applications.FindByIdAsync(context.ClientId); var application = await services.Applications.FindByClientIdAsync(context.ClientId);
if (application == null) { if (application == null) {
services.Logger.LogError("The authorization request was rejected because the client " + services.Logger.LogError("The authorization request was rejected because the client " +
"application was not found: '{ClientId}'.", context.ClientId); "application was not found: '{ClientId}'.", context.ClientId);

4
src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Exchange.cs

@ -72,7 +72,7 @@ namespace OpenIddict.Infrastructure {
} }
// Retrieve the application details corresponding to the requested client_id. // Retrieve the application details corresponding to the requested client_id.
var application = await services.Applications.FindByIdAsync(context.ClientId); var application = await services.Applications.FindByClientIdAsync(context.ClientId);
if (application == null) { if (application == null) {
services.Logger.LogError("The token request was rejected because the client " + services.Logger.LogError("The token request was rejected because the client " +
"application was not found: '{ClientId}'.", context.ClientId); "application was not found: '{ClientId}'.", context.ClientId);
@ -132,7 +132,7 @@ namespace OpenIddict.Infrastructure {
// Retrieve the application details corresponding to the requested client_id. // Retrieve the application details corresponding to the requested client_id.
// Note: this call shouldn't return a null instance, but a race condition may occur // Note: this call shouldn't return a null instance, but a race condition may occur
// if the application was removed after the initial check made by ValidateTokenRequest. // if the application was removed after the initial check made by ValidateTokenRequest.
var application = await services.Applications.FindByIdAsync(context.ClientId); var application = await services.Applications.FindByClientIdAsync(context.ClientId);
if (application == null) { if (application == null) {
throw new InvalidOperationException("The token request was aborted because the client application corresponding " + throw new InvalidOperationException("The token request was aborted because the client application corresponding " +
$"to the '{context.ClientId}' identifier was not found in the database."); $"to the '{context.ClientId}' identifier was not found in the database.");

2
src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Introspection.cs

@ -42,7 +42,7 @@ namespace OpenIddict.Infrastructure {
} }
// Retrieve the application details corresponding to the requested client_id. // Retrieve the application details corresponding to the requested client_id.
var application = await services.Applications.FindByIdAsync(context.ClientId); var application = await services.Applications.FindByClientIdAsync(context.ClientId);
if (application == null) { if (application == null) {
services.Logger.LogError("The introspection request was rejected because the client " + services.Logger.LogError("The introspection request was rejected because the client " +
"application was not found: '{ClientId}'.", context.ClientId); "application was not found: '{ClientId}'.", context.ClientId);

2
src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Revocation.cs

@ -45,7 +45,7 @@ namespace OpenIddict.Infrastructure {
} }
// Retrieve the application details corresponding to the requested client_id. // Retrieve the application details corresponding to the requested client_id.
var application = await services.Applications.FindByIdAsync(context.ClientId); var application = await services.Applications.FindByClientIdAsync(context.ClientId);
if (application == null) { if (application == null) {
context.Reject( context.Reject(
error: OpenIdConnectConstants.Errors.InvalidClient, error: OpenIdConnectConstants.Errors.InvalidClient,

2
src/OpenIddict.Core/Infrastructure/OpenIddictProvider.Serialization.cs

@ -40,7 +40,7 @@ namespace OpenIddict.Infrastructure {
// If the client application sending the token request is known, // If the client application sending the token request is known,
// ensure the token is attached to the corresponding client entity. // ensure the token is attached to the corresponding client entity.
if (!string.IsNullOrEmpty(context.Request.ClientId)) { if (!string.IsNullOrEmpty(context.Request.ClientId)) {
var application = await services.Applications.FindByIdAsync(context.Request.ClientId); var application = await services.Applications.FindByClientIdAsync(context.Request.ClientId);
if (application == null) { if (application == null) {
throw new InvalidOperationException("The application cannot be retrieved from the database."); throw new InvalidOperationException("The application cannot be retrieved from the database.");
} }

12
src/OpenIddict.Core/Managers/OpenIddictApplicationManager.cs

@ -77,6 +77,18 @@ namespace OpenIddict {
return Store.FindByIdAsync(identifier, CancellationToken); return Store.FindByIdAsync(identifier, CancellationToken);
} }
/// <summary>
/// Retrieves an application using its client identifier.
/// </summary>
/// <param name="identifier">The client identifier associated with the application.</param>
/// <returns>
/// A <see cref="Task"/> that can be used to monitor the asynchronous operation,
/// whose result returns the client application corresponding to the identifier.
/// </returns>
public virtual Task<TApplication> FindByClientIdAsync(string identifier) {
return Store.FindByClientIdAsync(identifier, CancellationToken);
}
/// <summary> /// <summary>
/// Retrieves an application using its post_logout_redirect_uri. /// Retrieves an application using its post_logout_redirect_uri.
/// </summary> /// </summary>

11
src/OpenIddict.Core/Stores/IOpenIddictApplicationStore.cs

@ -36,6 +36,17 @@ namespace OpenIddict {
/// </returns> /// </returns>
Task<TApplication> FindByIdAsync(string identifier, CancellationToken cancellationToken); Task<TApplication> FindByIdAsync(string identifier, CancellationToken cancellationToken);
/// <summary>
/// Retrieves an application using its client identifier.
/// </summary>
/// <param name="identifier">The client identifier associated with the application.</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 client application corresponding to the identifier.
/// </returns>
Task<TApplication> FindByClientIdAsync(string identifier, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Retrieves an application using its post_logout_redirect_uri. /// Retrieves an application using its post_logout_redirect_uri.
/// </summary> /// </summary>

18
src/OpenIddict.EntityFramework/Models/OpenIddictApplication.cs

@ -28,6 +28,18 @@ namespace OpenIddict {
/// Represents an OpenIddict application. /// Represents an OpenIddict application.
/// </summary> /// </summary>
public class OpenIddictApplication<TKey, TToken> where TKey : IEquatable<TKey> { public class OpenIddictApplication<TKey, TToken> where TKey : IEquatable<TKey> {
/// <summary>
/// Gets or sets the client identifier
/// associated with the current application.
/// </summary>
public virtual string ClientId { get; set; }
/// <summary>
/// Gets or sets the hashed client secret
/// associated with the current application.
/// </summary>
public virtual string ClientSecret { get; set; }
/// <summary> /// <summary>
/// Gets or sets the display name /// Gets or sets the display name
/// associated with the current application. /// associated with the current application.
@ -52,12 +64,6 @@ namespace OpenIddict {
/// </summary> /// </summary>
public virtual string RedirectUri { get; set; } public virtual string RedirectUri { get; set; }
/// <summary>
/// Gets or sets the hashed secret
/// associated with the current application.
/// </summary>
public virtual string Secret { get; set; }
/// <summary> /// <summary>
/// Gets the list of the tokens associated with this application. /// Gets the list of the tokens associated with this application.
/// </summary> /// </summary>

3
src/OpenIddict.EntityFramework/OpenIddictContext.cs

@ -158,6 +158,9 @@ namespace OpenIddict {
builder.Entity<TApplication>(entity => { builder.Entity<TApplication>(entity => {
entity.HasKey(application => application.Id); entity.HasKey(application => application.Id);
entity.HasIndex(application => application.ClientId)
.IsUnique(unique: true);
entity.HasMany(application => application.Tokens) entity.HasMany(application => application.Tokens)
.WithOne() .WithOne()
.HasForeignKey("ApplicationId") .HasForeignKey("ApplicationId")

15
src/OpenIddict.EntityFramework/Stores/OpenIddictApplicationStore.cs

@ -86,6 +86,19 @@ namespace OpenIddict {
return Applications.SingleOrDefaultAsync(application => application.Id.Equals(key), cancellationToken); return Applications.SingleOrDefaultAsync(application => application.Id.Equals(key), cancellationToken);
} }
/// <summary>
/// Retrieves an application using its client identifier.
/// </summary>
/// <param name="identifier">The client identifier associated with the application.</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 client application corresponding to the identifier.
/// </returns>
public virtual Task<TApplication> FindByClientIdAsync(string identifier, CancellationToken cancellationToken) {
return Applications.SingleOrDefaultAsync(application => application.ClientId.Equals(identifier), cancellationToken);
}
/// <summary> /// <summary>
/// Retrieves an application using its post_logout_redirect_uri. /// Retrieves an application using its post_logout_redirect_uri.
/// </summary> /// </summary>
@ -147,7 +160,7 @@ namespace OpenIddict {
throw new ArgumentNullException(nameof(application)); throw new ArgumentNullException(nameof(application));
} }
return Task.FromResult(application.Secret); return Task.FromResult(application.ClientSecret);
} }
/// <summary> /// <summary>

6
src/OpenIddict.EntityFramework/Stores/OpenIddictUserStore.cs

@ -103,13 +103,11 @@ namespace OpenIddict {
// Ensure that the key type can be serialized. // Ensure that the key type can be serialized.
var converter = TypeDescriptor.GetConverter(typeof(TKey)); var converter = TypeDescriptor.GetConverter(typeof(TKey));
if (!converter.CanConvertTo(typeof(string)) || !converter.CanConvertFrom(typeof(string))) { if (!converter.CanConvertTo(typeof(string))) {
throw new InvalidOperationException($"The '{typeof(TKey).Name}' key type is not supported."); throw new InvalidOperationException($"The '{typeof(TKey).Name}' key type is not supported.");
} }
var key = (TKey) converter.ConvertFromInvariantString(client); var application = await Applications.FirstOrDefaultAsync(entity => entity.ClientId.Equals(client), cancellationToken);
var application = await Applications.FirstOrDefaultAsync(entity => entity.Id.Equals(key), cancellationToken);
if (application == null) { if (application == null) {
throw new InvalidOperationException("The application cannot be found in the database."); throw new InvalidOperationException("The application cannot be found in the database.");
} }

4
src/OpenIddict.Mvc/OpenIddictController.cs

@ -57,7 +57,7 @@ namespace OpenIddict.Mvc {
// Note: AspNet.Security.OpenIdConnect.Server automatically ensures an application // Note: AspNet.Security.OpenIdConnect.Server automatically ensures an application
// corresponds to the client_id specified in the authorization request using // corresponds to the client_id specified in the authorization request using
// IOpenIdConnectServerProvider.ValidateAuthorizationRequest (see OpenIddictProvider.cs). // IOpenIdConnectServerProvider.ValidateAuthorizationRequest (see OpenIddictProvider.cs).
var application = await applications.FindByIdAsync(request.ClientId); var application = await applications.FindByClientIdAsync(request.ClientId);
if (application == null) { if (application == null) {
return View("Error", new OpenIdConnectMessage { return View("Error", new OpenIdConnectMessage {
Error = OpenIdConnectConstants.Errors.InvalidClient, Error = OpenIdConnectConstants.Errors.InvalidClient,
@ -100,7 +100,7 @@ namespace OpenIddict.Mvc {
var identity = await users.CreateIdentityAsync(user, request.GetScopes()); var identity = await users.CreateIdentityAsync(user, request.GetScopes());
Debug.Assert(identity != null); Debug.Assert(identity != null);
var application = await applications.FindByIdAsync(request.ClientId); var application = await applications.FindByClientIdAsync(request.ClientId);
if (application == null) { if (application == null) {
return View("Error", new OpenIdConnectMessage { return View("Error", new OpenIdConnectMessage {
Error = OpenIdConnectConstants.Errors.InvalidClient, Error = OpenIdConnectConstants.Errors.InvalidClient,

Loading…
Cancel
Save