diff --git a/docs/docs/v4/welcome/migrate-quick-steps.md b/docs/docs/v4/welcome/migrate-quick-steps.md index b24c3be4..3a4a5406 100644 --- a/docs/docs/v4/welcome/migrate-quick-steps.md +++ b/docs/docs/v4/welcome/migrate-quick-steps.md @@ -1251,7 +1251,393 @@ For additional information, see the updated [Security documentation](../security The CredHub client has been removed from Steeltoe in v4. Use [CredHub Service Broker](https://techdocs.broadcom.com/us/en/vmware-tanzu/platform-services/credhub-service-broker/services/credhub-sb/index.html) instead. -### TODO: JWT/OAuth/OpenID Connect/Certificates... +### OAuth + +OAuth support has been removed from Steeltoe in v4. Use OpenID Connect instead. + +Before migrating to Steeltoe v4, apply the following changes to migrate from OAuth to OpenID Connect using Steeltoe v3. + +appsettings.json: + +```diff +{ + "$schema": "https://steeltoe.io/schema/v3/schema.json", + "Security": { + "Oauth2": { + "Client": { +- "AuthDomain": "http://localhost:8080", ++ "Authority": "http://localhost:8080", ++ "MetadataAddress": "http://localhost:8080/.well-known/openid-configuration", ++ "RequireHttpsMetadata": false, ++ "AdditionalScopes": "sampleapi.read", + "CallbackPath": "/signin-oidc", + "ClientId": "steeltoesamplesclient", + "ClientSecret": "client_secret" + } + } + } +} +``` + +> [!NOTE] +> Depending on your application's needs, you may need to add scopes to the application's configuration that did not previously need to be specified. + +program.cs: + +```diff +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.HttpOverrides; +using Steeltoe.Extensions.Configuration.CloudFoundry; +using Steeltoe.Security.Authentication.CloudFoundry; + +var builder = WebApplication.CreateBuilder(args); +builder.AddCloudFoundryConfiguration(); +builder.Services.AddAuthentication(options => + { + options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = CloudFoundryDefaults.AuthenticationScheme; + }) + .AddCookie(options => options.AccessDeniedPath = new PathString("/Home/AccessDenied")) +- .AddCloudFoundryOAuth(builder.Configuration); ++ .AddCloudFoundryOpenIdConnect(builder.Configuration); +builder.Services.AddAuthorizationBuilder() + .AddPolicy("read", policy => policy.RequireClaim("scope", "sampleapi.read")); + +var app = builder.Build(); + +app.UseForwardedHeaders(new ForwardedHeadersOptions +{ + ForwardedHeaders = ForwardedHeaders.XForwardedProto +}); + +app.UseAuthentication(); +app.UseAuthorization(); + +app.MapGet("/test-auth", async httpContext => + { + httpContext.Response.StatusCode = 200; + httpContext.Response.ContentType = "text/plain"; + await httpContext.Response.WriteAsync("You are logged in and carry the required claim."); + }).RequireAuthorization("read"); + +app.Run(); +``` + +### OpenID Connect + +Project file: + +```diff + + +- ++ +- ++ + + +``` + +appsettings.json: + +```diff +{ +- "$schema": "https://steeltoe.io/schema/v3/schema.json", ++ "$schema": "https://steeltoe.io/schema/v4/schema.json", +- "Security": { +- "Oauth2": { +- "Client": { +- "Authority": "http://localhost:8080", +- "MetadataAddress": "http://localhost:8080/.well-known/openid-configuration", +- "RequireHttpsMetadata": false, +- "AdditionalScopes": "sampleapi.read", +- "CallbackPath": "/signin-oidc", +- "ClientId": "steeltoesamplesclient", +- "ClientSecret": "client_secret" +- } +- } +- } ++ "Authentication": { ++ "Schemes": { ++ "OpenIdConnect": { ++ "Authority": "http://localhost:8080", ++ "MetadataAddress": "http://localhost:8080/.well-known/openid-configuration", ++ "RequireHttpsMetadata": false, ++ "Scope": [ "openid", "sampleapi.read" ], ++ "CallbackPath": "/signin-oidc", ++ "ClientId": "steeltoesamplesclient", ++ "ClientSecret": "client_secret" ++ } ++ } ++ } +} +``` + +> [!NOTE] +> This is not a complete listing of appsettings. As of version 4, Steeltoe configures Microsoft's option class rather than maintaining separate options. +> Refer to [the OpenIdConnectOptions class documentation](https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.authentication.openidconnect.openidconnectoptions) for the new options. + +Program.cs: + +```diff +using Microsoft.AspNetCore.Authentication.Cookies; +-using Microsoft.AspNetCore.HttpOverrides; +-using Steeltoe.Extensions.Configuration.CloudFoundry; ++using Steeltoe.Configuration.CloudFoundry; ++using Steeltoe.Configuration.CloudFoundry.ServiceBindings; +-using Steeltoe.Security.Authentication.CloudFoundry; ++using Microsoft.AspNetCore.Authentication.OpenIdConnect; ++using Steeltoe.Security.Authentication.OpenIdConnect; + +var builder = WebApplication.CreateBuilder(args); +builder.AddCloudFoundryConfiguration(); ++builder.Configuration.AddCloudFoundryServiceBindings(); +builder.Services.AddAuthentication(options => + { + options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; +- options.DefaultChallengeScheme = CloudFoundryDefaults.AuthenticationScheme; ++ options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; + }) + .AddCookie(options => options.AccessDeniedPath = new PathString("/Home/AccessDenied")) +- .AddCloudFoundryOpenIdConnect(builder.Configuration); ++ .AddOpenIdConnect().ConfigureOpenIdConnectForCloudFoundry(); +builder.Services.AddAuthorizationBuilder() + .AddPolicy("read", policy => policy.RequireClaim("scope", "sampleapi.read")); + +var app = builder.Build(); + +-app.UseForwardedHeaders(new ForwardedHeadersOptions +-{ +- ForwardedHeaders = ForwardedHeaders.XForwardedProto +-}); + +app.UseAuthentication(); +app.UseAuthorization(); + +app.MapGet("/test-auth", async httpContext => + { + httpContext.Response.StatusCode = 200; + httpContext.Response.ContentType = "text/plain"; + await httpContext.Response.WriteAsync("You are logged in and carry the required claim."); + }).RequireAuthorization("read"); + +app.Run(); +``` + +### JWT Bearer + +Project file: + +```diff + + +- ++ +- ++ + + +``` + +appsettings.json: + +```diff +{ +- "$schema": "https://steeltoe.io/schema/v3/schema.json", ++ "$schema": "https://steeltoe.io/schema/v4/schema.json", +- "Security": { +- "Oauth2": { +- "Client": { +- "AuthDomain": "http://localhost:8080", +- "MetadataAddress": "http://localhost:8080/.well-known/openid-configuration", +- "RequireHttpsMetadata": false, +- "ClientId": "steeltoesamplesserver", +- "ClientSecret": "server_secret" +- } +- } +- } ++ "Authentication": { ++ "Schemes": { ++ "Bearer": { ++ "Authority": "http://localhost:8080", ++ "MetadataAddress": "http://localhost:8080/.well-known/openid-configuration", ++ "RequireHttpsMetadata": false, ++ "ClientId": "steeltoesamplesserver", ++ "ClientSecret": "server_secret", ++ "ValidAudiences": [ "sampleapi" ] ++ } ++ } ++ } +} +``` + +> [!NOTE] +> This is not a complete listing of appsettings. As of version 4, Steeltoe configures Microsoft's option class rather than maintaining separate options. +> Refer to [the JwtBearerOptions class documentation](https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.authentication.jwtbearer.jwtbeareroptions) for the new options. + +Program.cs: + +```diff +-using Microsoft.AspNetCore.HttpOverrides; +-using Steeltoe.Extensions.Configuration.CloudFoundry; ++using Steeltoe.Configuration.CloudFoundry; ++using Steeltoe.Configuration.CloudFoundry.ServiceBindings; +-using Steeltoe.Security.Authentication.CloudFoundry; ++using Steeltoe.Security.Authentication.JwtBearer; + +var builder = WebApplication.CreateBuilder(args); + +builder.AddCloudFoundryConfiguration(); ++builder.Configuration.AddCloudFoundryServiceBindings(); +builder.Services.AddAuthentication() +- .AddCloudFoundryJwtBearer(builder.Configuration); ++ .AddJwtBearer().ConfigureJwtBearerForCloudFoundry(); +builder.Services.AddAuthorizationBuilder() + .AddPolicy("sampleapi.read", policy => policy.RequireClaim("scope", "sampleapi.read")); + +var app = builder.Build(); + +-app.UseForwardedHeaders(new ForwardedHeadersOptions +-{ +- ForwardedHeaders = ForwardedHeaders.XForwardedProto +-}); + +app.UseAuthentication(); +app.UseAuthorization(); + +app.MapGet("/test-jwt", async httpContext => + { + httpContext.Response.StatusCode = 200; + httpContext.Response.ContentType = "text/plain"; + await httpContext.Response.WriteAsync("JWT is valid and contains the required claim."); + }).RequireAuthorization("sampleapi.read"); + +app.Run(); +``` + +### Client Certificates (Mutual TLS) + +Project file: + +```diff + + +- ++ + + +``` + +launchsettings.json (server-side): + +```diff +{ + "profiles": { + "http": { + "commandName": "Project", + "applicationUrl": "https://localhost:7107" + } + } +} +``` + +Program.cs (server-side): + +```diff ++using Steeltoe.Common.Certificates; +-using Steeltoe.Security.Authentication.CloudFoundry; ++using Steeltoe.Security.Authorization.Certificate; + +const string orgId = "a8fef16f-94c0-49e3-aa0b-ced7c3da6229"; +const string spaceId = "122b942a-d7b9-4839-b26e-836654b9785f"; + +var builder = WebApplication.CreateBuilder(args); +-builder.Configuration.AddCloudFoundryContainerIdentity(orgId, spaceId); ++builder.Configuration.AddAppInstanceIdentityCertificate(new Guid(orgId), new Guid(spaceId)); +-builder.Services.AddCloudFoundryCertificateAuth(options => options.CertificateHeader = "X-Forwarded-Client-Cert"); ++builder.Services.AddAuthentication().AddCertificate(); ++builder.Services.AddAuthorizationBuilder().AddOrgAndSpacePolicies("X-Forwarded-Client-Cert"); + +var app = builder.Build(); + +-app.UseCloudFoundryCertificateAuth(); ++app.UseCertificateAuthorization(); + +app.MapGet("/test-same-org", async httpContext => + { + httpContext.Response.StatusCode = 200; + httpContext.Response.ContentType = "text/plain"; + await httpContext.Response.WriteAsync("Client and server identity certificates have matching Org values."); + }) +- .RequireAuthorization(CloudFoundryDefaults.SameOrganizationAuthorizationPolicy); ++ .RequireAuthorization(CertificateAuthorizationPolicies.SameOrg); +app.MapGet("/test-same-space", async httpContext => + { + httpContext.Response.StatusCode = 200; + httpContext.Response.ContentType = "text/plain"; + await httpContext.Response.WriteAsync("Client and server identity certificates have matching Space values."); + }) +- .RequireAuthorization(CloudFoundryDefaults.SameSpaceAuthorizationPolicy); ++ .RequireAuthorization(CertificateAuthorizationPolicies.SameSpace); + +app.Run(); +``` + +> [!NOTE] +> Prior to Steeltoe 3.3.0, Steeltoe Certificate Auth used the header `X-Forwarded-Client-Cert`, which was not configurable. +> The code shown above is provided for compatibility between the versions. The preferred header name is `X-Client-Cert`. +> In Steeltoe 4.0, the default header is `X-Client-Cert`, so the parameter can be omitted if cross-compatibility is not required. + +Program.cs (client-side): + +```diff +-using System.Security.Cryptography.X509Certificates; +-using Microsoft.Extensions.Options; +-using Steeltoe.Common.Options; ++using Steeltoe.Common.Certificates; +-using Steeltoe.Security.Authentication.CloudFoundry; ++using Steeltoe.Security.Authorization.Certificate; + +const string orgId = "a8fef16f-94c0-49e3-aa0b-ced7c3da6229"; +const string spaceId = "122b942a-d7b9-4839-b26e-836654b9785f"; + +var builder = WebApplication.CreateBuilder(args); + +-builder.Configuration.AddCloudFoundryContainerIdentity(orgId, spaceId); ++builder.Configuration.AddAppInstanceIdentityCertificate(new Guid(orgId), new Guid(spaceId)); +-builder.Services.AddCloudFoundryContainerIdentity(); +builder.Services +- .AddHttpClient((services, client) => +- { +- client.BaseAddress = new Uri("https://localhost:7107"); +- var options = services.GetRequiredService>(); +- var b64 = Convert.ToBase64String(options.Value.Certificate.Export(X509ContentType.Cert)); +- client.DefaultRequestHeaders.Add("X-Forwarded-Client-Cert", b64); +- }); ++ .AddHttpClient(httpClient => httpClient.BaseAddress = new Uri("https://localhost:7107")) ++ .AddAppInstanceIdentityCertificate("X-Forwarded-Client-Cert"); + +var app = builder.Build(); + +var testClient = app.Services.GetRequiredService(); +string orgResponse = await testClient.GetAsync("/test-same-org"); +Console.WriteLine($"Org response: {orgResponse}"); +string spaceResponse = await testClient.GetAsync("/test-same-space"); +Console.WriteLine($"Space response: {spaceResponse}"); + +public class TestClient(HttpClient httpClient) +{ + public async Task GetAsync(string requestPath) + { + return await httpClient.GetStringAsync(requestPath); + } +} +``` + +> [!NOTE] +> Prior to Steeltoe 3.3.0, Steeltoe Certificate Auth used the header `X-Forwarded-Client-Cert`, which was not configurable. +> The code shown above is provided for compatibility between the versions. The preferred header name is `X-Client-Cert`. +> In Steeltoe 4.0, the default header is `X-Client-Cert`, so the parameter can be omitted if cross-compatibility is not required. ### DataProtection Key Store using Redis/Valkey