Skip to content

Commit

Permalink
Added additional OpenId Introspection/Revocation endpoints and PKCE t…
Browse files Browse the repository at this point in the history
…o the OpenID Settings and Application UI pages (#11903)
  • Loading branch information
mcalasa authored Jul 9, 2022
1 parent e80384d commit 4944fb1
Show file tree
Hide file tree
Showing 19 changed files with 179 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -79,19 +79,32 @@ public void Configure(OpenIddictServerOptions options)
{
options.AuthorizationEndpointUris.Add(new Uri(settings.AuthorizationEndpointPath.Value, UriKind.Relative));
}

if (settings.LogoutEndpointPath.HasValue)
{
options.LogoutEndpointUris.Add(new Uri(settings.LogoutEndpointPath.Value, UriKind.Relative));
}

if (settings.TokenEndpointPath.HasValue)
{
options.TokenEndpointUris.Add(new Uri(settings.TokenEndpointPath.Value, UriKind.Relative));
}

if (settings.UserinfoEndpointPath.HasValue)
{
options.UserinfoEndpointUris.Add(new Uri(settings.UserinfoEndpointPath.Value, UriKind.Relative));
}

if (settings.IntrospectionEndpointPath.HasValue)
{
options.IntrospectionEndpointUris.Add(new Uri(settings.IntrospectionEndpointPath.Value, UriKind.Relative));
}

if (settings.RevocationEndpointPath.HasValue)
{
options.RevocationEndpointUris.Add(new Uri(settings.RevocationEndpointPath.Value, UriKind.Relative));
}

// For now, response types and response modes are not directly
// configurable and are inferred from the selected flows.
if (settings.AllowAuthorizationCodeFlow)
Expand All @@ -106,10 +119,12 @@ public void Configure(OpenIddictServerOptions options)

options.ResponseTypes.Add(ResponseTypes.Code);
}

if (settings.AllowClientCredentialsFlow)
{
options.GrantTypes.Add(GrantTypes.ClientCredentials);
}

if (settings.AllowHybridFlow)
{
options.CodeChallengeMethods.Add(CodeChallengeMethods.Sha256);
Expand All @@ -124,6 +139,7 @@ public void Configure(OpenIddictServerOptions options)
options.ResponseTypes.Add(ResponseTypes.Code + ' ' + ResponseTypes.IdToken + ' ' + ResponseTypes.Token);
options.ResponseTypes.Add(ResponseTypes.Code + ' ' + ResponseTypes.Token);
}

if (settings.AllowImplicitFlow)
{
options.GrantTypes.Add(GrantTypes.Implicit);
Expand All @@ -135,17 +151,21 @@ public void Configure(OpenIddictServerOptions options)
options.ResponseTypes.Add(ResponseTypes.IdToken + ' ' + ResponseTypes.Token);
options.ResponseTypes.Add(ResponseTypes.Token);
}

if (settings.AllowPasswordFlow)
{
options.GrantTypes.Add(GrantTypes.Password);
}

if (settings.AllowRefreshTokenFlow)
{
options.GrantTypes.Add(GrantTypes.RefreshToken);

options.Scopes.Add(Scopes.OfflineAccess);
}

options.RequireProofKeyForCodeExchange = settings.RequireProofKeyForCodeExchange;

options.Scopes.Add(Scopes.Email);
options.Scopes.Add(Scopes.Phone);
options.Scopes.Add(Scopes.Profile);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,11 @@ public async Task<IActionResult> Create(CreateOpenIdApplicationViewModel model,
AllowClientCredentialsFlow = model.AllowClientCredentialsFlow,
AllowHybridFlow = model.AllowHybridFlow,
AllowImplicitFlow = model.AllowImplicitFlow,
AllowIntrospectionEndpoint = model.AllowIntrospectionEndpoint,
AllowLogoutEndpoint = model.AllowLogoutEndpoint,
AllowPasswordFlow = model.AllowPasswordFlow,
AllowRefreshTokenFlow = model.AllowRefreshTokenFlow,
AllowRevocationEndpoint = model.AllowRevocationEndpoint,
ClientId = model.ClientId,
ClientSecret = model.ClientSecret,
ConsentType = model.ConsentType,
Expand All @@ -174,7 +176,8 @@ public async Task<IActionResult> Create(CreateOpenIdApplicationViewModel model,
RedirectUris = model.RedirectUris,
Roles = model.RoleEntries.Where(x => x.Selected).Select(x => x.Name).ToArray(),
Scopes = model.ScopeEntries.Where(x => x.Selected).Select(x => x.Name).ToArray(),
Type = model.Type
Type = model.Type,
RequireProofKeyForCodeExchange = model.RequireProofKeyForCodeExchange
};

await _applicationManager.UpdateDescriptorFromSettings(settings);
Expand All @@ -201,6 +204,7 @@ public async Task<IActionResult> Edit(string id, string returnUrl = null)
}

ValueTask<bool> HasPermissionAsync(string permission) => _applicationManager.HasPermissionAsync(application, permission);
ValueTask<bool> HasRequirementAsync(string requirement) => _applicationManager.HasRequirementAsync(application, requirement);

var model = new EditOpenIdApplicationViewModel
{
Expand All @@ -226,13 +230,16 @@ await HasPermissionAsync(OpenIddictConstants.Permissions.ResponseTypes.Token)),
AllowPasswordFlow = await HasPermissionAsync(OpenIddictConstants.Permissions.GrantTypes.Password),
AllowRefreshTokenFlow = await HasPermissionAsync(OpenIddictConstants.Permissions.GrantTypes.RefreshToken),
AllowLogoutEndpoint = await HasPermissionAsync(OpenIddictConstants.Permissions.Endpoints.Logout),
AllowIntrospectionEndpoint = await HasPermissionAsync(OpenIddictConstants.Permissions.Endpoints.Introspection),
AllowRevocationEndpoint = await HasPermissionAsync(OpenIddictConstants.Permissions.Endpoints.Revocation),
ClientId = await _applicationManager.GetClientIdAsync(application),
ConsentType = await _applicationManager.GetConsentTypeAsync(application),
DisplayName = await _applicationManager.GetDisplayNameAsync(application),
Id = await _applicationManager.GetPhysicalIdAsync(application),
PostLogoutRedirectUris = string.Join(" ", await _applicationManager.GetPostLogoutRedirectUrisAsync(application)),
RedirectUris = string.Join(" ", await _applicationManager.GetRedirectUrisAsync(application)),
Type = await _applicationManager.GetClientTypeAsync(application)
Type = await _applicationManager.GetClientTypeAsync(application),
RequireProofKeyForCodeExchange = await HasRequirementAsync(OpenIddictConstants.Requirements.Features.ProofKeyForCodeExchange)
};

var roleService = HttpContext.RequestServices?.GetService<IRoleService>();
Expand Down Expand Up @@ -322,9 +329,11 @@ await _applicationManager.GetIdAsync(application), StringComparison.Ordinal))
AllowClientCredentialsFlow = model.AllowClientCredentialsFlow,
AllowHybridFlow = model.AllowHybridFlow,
AllowImplicitFlow = model.AllowImplicitFlow,
AllowIntrospectionEndpoint = model.AllowIntrospectionEndpoint,
AllowLogoutEndpoint = model.AllowLogoutEndpoint,
AllowPasswordFlow = model.AllowPasswordFlow,
AllowRefreshTokenFlow = model.AllowRefreshTokenFlow,
AllowRevocationEndpoint = model.AllowRevocationEndpoint,
ClientId = model.ClientId,
ClientSecret = model.ClientSecret,
ConsentType = model.ConsentType,
Expand All @@ -333,7 +342,8 @@ await _applicationManager.GetIdAsync(application), StringComparison.Ordinal))
RedirectUris = model.RedirectUris,
Roles = model.RoleEntries.Where(x => x.Selected).Select(x => x.Name).ToArray(),
Scopes = model.ScopeEntries.Where(x => x.Selected).Select(x => x.Name).ToArray(),
Type = model.Type
Type = model.Type,
RequireProofKeyForCodeExchange = model.RequireProofKeyForCodeExchange
};

await _applicationManager.UpdateDescriptorFromSettings(settings, application);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlan
EnableLogoutEndpoint = !string.IsNullOrWhiteSpace(settings.LogoutEndpointPath),
EnableTokenEndpoint = !string.IsNullOrWhiteSpace(settings.TokenEndpointPath),
EnableUserInfoEndpoint = !string.IsNullOrWhiteSpace(settings.UserinfoEndpointPath),
EnableIntrospectionEndpoint = !string.IsNullOrWhiteSpace(settings.IntrospectionEndpointPath),
EnableRevocationEndpoint = !string.IsNullOrWhiteSpace(settings.RevocationEndpointPath),

AllowAuthorizationCodeFlow = settings.AllowAuthorizationCodeFlow,
AllowClientCredentialsFlow = settings.AllowClientCredentialsFlow,
Expand All @@ -58,6 +60,7 @@ public async Task ProcessDeploymentStepAsync(DeploymentStep step, DeploymentPlan
DisableAccessTokenEncryption = settings.DisableAccessTokenEncryption,
DisableRollingRefreshTokens = settings.DisableRollingRefreshTokens,
UseReferenceAccessTokens = settings.UseReferenceAccessTokens,
RequireProofKeyForCodeExchange = settings.RequireProofKeyForCodeExchange,
};

// Use nameof(OpenIdServerSettings) as name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ public override Task<IDisplayResult> EditAsync(OpenIdServerSettings settings, Bu
model.EnableLogoutEndpoint = settings.LogoutEndpointPath.HasValue;
model.EnableTokenEndpoint = settings.TokenEndpointPath.HasValue;
model.EnableUserInfoEndpoint = settings.UserinfoEndpointPath.HasValue;
model.EnableIntrospectionEndpoint = settings.IntrospectionEndpointPath.HasValue;
model.EnableRevocationEndpoint = settings.RevocationEndpointPath.HasValue;
model.AllowAuthorizationCodeFlow = settings.AllowAuthorizationCodeFlow;
model.AllowClientCredentialsFlow = settings.AllowClientCredentialsFlow;
Expand All @@ -46,6 +48,7 @@ public override Task<IDisplayResult> EditAsync(OpenIdServerSettings settings, Bu
model.DisableAccessTokenEncryption = settings.DisableAccessTokenEncryption;
model.DisableRollingRefreshTokens = settings.DisableRollingRefreshTokens;
model.UseReferenceAccessTokens = settings.UseReferenceAccessTokens;
model.RequireProofKeyForCodeExchange = settings.RequireProofKeyForCodeExchange;
foreach (var (certificate, location, name) in await _serverService.GetAvailableCertificatesAsync())
{
Expand Down Expand Up @@ -89,6 +92,10 @@ public override async Task<IDisplayResult> UpdateAsync(OpenIdServerSettings sett
new PathString("/connect/token") : PathString.Empty;
settings.UserinfoEndpointPath = model.EnableUserInfoEndpoint ?
new PathString("/connect/userinfo") : PathString.Empty;
settings.IntrospectionEndpointPath = model.EnableIntrospectionEndpoint ?
new PathString("/connect/introspect") : PathString.Empty;
settings.RevocationEndpointPath = model.EnableRevocationEndpoint ?
new PathString("/connect/revoke") : PathString.Empty;

settings.AllowAuthorizationCodeFlow = model.AllowAuthorizationCodeFlow;
settings.AllowClientCredentialsFlow = model.AllowClientCredentialsFlow;
Expand All @@ -100,6 +107,7 @@ public override async Task<IDisplayResult> UpdateAsync(OpenIdServerSettings sett
settings.DisableAccessTokenEncryption = model.DisableAccessTokenEncryption;
settings.DisableRollingRefreshTokens = model.DisableRollingRefreshTokens;
settings.UseReferenceAccessTokens = model.UseReferenceAccessTokens;
settings.RequireProofKeyForCodeExchange = model.RequireProofKeyForCodeExchange;

return await EditAsync(settings, context);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ public class OpenIdApplicationSettings
public bool AllowHybridFlow { get; set; }
public bool AllowImplicitFlow { get; set; }
public bool AllowLogoutEndpoint { get; set; }
public bool AllowIntrospectionEndpoint { get; set; }
public bool AllowRevocationEndpoint { get; set; }
public bool RequireProofKeyForCodeExchange { get; set; }
}

internal static class OpenIdApplicationExtensions
Expand Down Expand Up @@ -158,6 +161,7 @@ public static async Task UpdateDescriptorFromSettings(this IOpenIdApplicationMan
descriptor.Permissions.Remove(OpenIddictConstants.Permissions.ResponseTypes.IdTokenToken);
descriptor.Permissions.Remove(OpenIddictConstants.Permissions.ResponseTypes.Token);
}

if (model.AllowHybridFlow)
{
descriptor.Permissions.Add(OpenIddictConstants.Permissions.ResponseTypes.CodeIdToken);
Expand All @@ -180,6 +184,33 @@ public static async Task UpdateDescriptorFromSettings(this IOpenIdApplicationMan
descriptor.Permissions.Remove(OpenIddictConstants.Permissions.ResponseTypes.CodeToken);
}

if (model.AllowIntrospectionEndpoint)
{
descriptor.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Introspection);
}
else
{
descriptor.Permissions.Remove(OpenIddictConstants.Permissions.Endpoints.Introspection);
}

if (model.AllowRevocationEndpoint)
{
descriptor.Permissions.Add(OpenIddictConstants.Permissions.Endpoints.Revocation);
}
else
{
descriptor.Permissions.Remove(OpenIddictConstants.Permissions.Endpoints.Revocation);
}

if (model.RequireProofKeyForCodeExchange)
{
descriptor.Requirements.Add(OpenIddictConstants.Requirements.Features.ProofKeyForCodeExchange);
}
else
{
descriptor.Requirements.Remove(OpenIddictConstants.Requirements.Features.ProofKeyForCodeExchange);
}

descriptor.Roles.Clear();

foreach (var role in model.Roles)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,11 @@ public async Task ExecuteAsync(RecipeExecutionContext context)
AllowClientCredentialsFlow = model.AllowClientCredentialsFlow,
AllowHybridFlow = model.AllowHybridFlow,
AllowImplicitFlow = model.AllowImplicitFlow,
AllowIntrospectionEndpoint = model.AllowIntrospectionEndpoint,
AllowLogoutEndpoint = model.AllowLogoutEndpoint,
AllowPasswordFlow = model.AllowPasswordFlow,
AllowRefreshTokenFlow = model.AllowRefreshTokenFlow,
AllowRevocationEndpoint = model.AllowRevocationEndpoint,
ClientId = model.ClientId,
ClientSecret = model.ClientSecret,
ConsentType = model.ConsentType,
Expand All @@ -48,7 +50,8 @@ public async Task ExecuteAsync(RecipeExecutionContext context)
RedirectUris = model.RedirectUris,
Roles = model.RoleEntries.Select(x => x.Name).ToArray(),
Scopes = model.ScopeEntries.Select(x => x.Name).ToArray(),
Type = model.Type
Type = model.Type,
RequireProofKeyForCodeExchange = model.RequireProofKeyForCodeExchange,
};

await _applicationManager.UpdateDescriptorFromSettings(settings, app);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ public class OpenIdApplicationStepModel
public bool AllowHybridFlow { get; set; }
public bool AllowImplicitFlow { get; set; }
public bool AllowLogoutEndpoint { get; set; }
public bool AllowIntrospectionEndpoint { get; set; }
public bool AllowRevocationEndpoint { get; set; }
public bool RequireProofKeyForCodeExchange { get; set; }

public class RoleEntry
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ public async Task ExecuteAsync(RecipeExecutionContext context)
new PathString("/connect/token") : PathString.Empty;
settings.UserinfoEndpointPath = model.EnableUserInfoEndpoint ?
new PathString("/connect/userinfo") : PathString.Empty;
settings.IntrospectionEndpointPath = model.EnableIntrospectionEndpoint ?
new PathString("/connect/introspect") : PathString.Empty;
settings.RevocationEndpointPath = model.EnableRevocationEndpoint ?
new PathString("/connect/revoke") : PathString.Empty;

settings.AllowAuthorizationCodeFlow = model.AllowAuthorizationCodeFlow;
settings.AllowClientCredentialsFlow = model.AllowClientCredentialsFlow;
Expand All @@ -58,6 +62,7 @@ public async Task ExecuteAsync(RecipeExecutionContext context)
settings.DisableAccessTokenEncryption = model.DisableAccessTokenEncryption;
settings.DisableRollingRefreshTokens = model.DisableRollingRefreshTokens;
settings.UseReferenceAccessTokens = model.UseReferenceAccessTokens;
settings.RequireProofKeyForCodeExchange = model.RequireProofKeyForCodeExchange;

await _serverService.UpdateSettingsAsync(settings);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,16 @@ public class OpenIdServerSettingsStepModel
public bool EnableAuthorizationEndpoint { get; set; }
public bool EnableLogoutEndpoint { get; set; }
public bool EnableUserInfoEndpoint { get; set; }
public bool EnableIntrospectionEndpoint { get; set; }
public bool EnableRevocationEndpoint { get; set; }
public bool AllowPasswordFlow { get; set; }
public bool AllowClientCredentialsFlow { get; set; }
public bool AllowAuthorizationCodeFlow { get; set; }
public bool AllowRefreshTokenFlow { get; set; }
public bool AllowHybridFlow { get; set; }
public bool AllowImplicitFlow { get; set; }
public bool DisableRollingRefreshTokens { get; set; }
public bool RequireProofKeyForCodeExchange { get; set; }

public bool UseReferenceAccessTokens { get; set; }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ private OpenIdServerSettings GetSettingsFromContainer(ISite container)
}

// If the OpenID server settings haven't been populated yet, the authorization,
// logout, token and userinfo endpoints are assumed to be enabled by default.
// logout, token, userinfo, introspection and revocation endpoints are assumed to be enabled by default.
// In this case, only the authorization code and refresh token flows are used.
return new OpenIdServerSettings
{
Expand All @@ -78,7 +78,9 @@ private OpenIdServerSettings GetSettingsFromContainer(ISite container)
AuthorizationEndpointPath = "/connect/authorize",
LogoutEndpointPath = "/connect/logout",
TokenEndpointPath = "/connect/token",
UserinfoEndpointPath = "/connect/userinfo"
UserinfoEndpointPath = "/connect/userinfo",
IntrospectionEndpointPath = "/connect/introspect",
RevocationEndpointPath = "/connect/revoke"
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ public class OpenIdServerSettings

public PathString UserinfoEndpointPath { get; set; }

public PathString IntrospectionEndpointPath { get; set; }

public PathString RevocationEndpointPath { get; set; }

public bool AllowPasswordFlow { get; set; }
public bool AllowClientCredentialsFlow { get; set; }
public bool AllowAuthorizationCodeFlow { get; set; }
Expand All @@ -36,6 +40,8 @@ public class OpenIdServerSettings

public bool UseReferenceAccessTokens { get; set; }

public bool RequireProofKeyForCodeExchange { get; set; }

public enum TokenFormat
{
DataProtection = 0,
Expand Down
Loading

0 comments on commit 4944fb1

Please sign in to comment.