使用单个IdentityServer4保护多API的技术方案咨询
Got it, let's walk through how to secure all your APIs and their corresponding clients using a single IdentityServer4 instance. This setup will keep your auth centralized while ensuring each client only gets access to the APIs it needs.
1. Register API Resources in IdentityServer4
First, you need to define each API as a separate resource in your IdentityServer4 configuration. This tells IdentityServer which APIs exist and what permissions (scopes) they expose.
Create a Config.cs file (or use your existing configuration class) and add a method to return your API resources:
public static IEnumerable<ApiResource> GetApiResources() { return new List<ApiResource> { new ApiResource("customer_management_api", "Customer Management API") { Scopes = { "customer_management_api.read", "customer_management_api.write" } }, new ApiResource("human_resource_api", "Human Resource API") { Scopes = { "human_resource_api.read", "human_resource_api.write" } }, new ApiResource("dashboard_api", "Dashboard API") { Scopes = { "dashboard_api.read" } } }; }
Pro tip: Break down scopes into read/write granularity if you need fine-grained access control. For example, the iOS app might only need read access to the Customer Management API, while the JS app gets both read/write.
2. Configure Client Applications
Next, register each client application in IdentityServer4, specifying which API scopes they're allowed to access, along with their grant type (based on the client type):
Add this method to your Config.cs:
public static IEnumerable<Client> GetClients() { return new List<Client> { // Customer Management - JavaScript SPA new Client { ClientId = "customer_management_js", ClientName = "Customer Management JavaScript App", AllowedGrantTypes = GrantTypes.Code, RequirePkce = true, RequireClientSecret = false, RedirectUris = { "https://your-js-app-url/callback" }, PostLogoutRedirectUris = { "https://your-js-app-url/logout-callback" }, AllowedScopes = { "openid", "profile", "customer_management_api.read", "customer_management_api.write" }, AllowAccessTokensViaBrowser = true }, // Customer Management - iOS App new Client { ClientId = "customer_management_ios", ClientName = "Customer Management iOS App", AllowedGrantTypes = GrantTypes.Code, RequirePkce = true, RequireClientSecret = false, RedirectUris = { "your-ios-app-scheme://callback" }, PostLogoutRedirectUris = { "your-ios-app-scheme://logout-callback" }, AllowedScopes = { "openid", "profile", "customer_management_api.read" } }, // Customer Management - Android App new Client { ClientId = "customer_management_android", ClientName = "Customer Management Android App", AllowedGrantTypes = GrantTypes.Code, RequirePkce = true, RequireClientSecret = false, RedirectUris = { "com.your-android-app://callback" }, PostLogoutRedirectUris = { "com.your-android-app://logout-callback" }, AllowedScopes = { "openid", "profile", "customer_management_api.read", "customer_management_api.write" } }, // Human Resource - MVC Application new Client { ClientId = "human_resource_mvc", ClientName = "Human Resource MVC App", AllowedGrantTypes = GrantTypes.Code, ClientSecrets = { new Secret("your-mvc-client-secret".Sha256()) }, RedirectUris = { "https://your-mvc-app-url/signin-oidc" }, PostLogoutRedirectUris = { "https://your-mvc-app-url/signout-callback-oidc" }, AllowedScopes = { "openid", "profile", "human_resource_api.read", "human_resource_api.write" }, AllowOfflineAccess = true // If you need refresh tokens }, // Dashboard - Angular Application new Client { ClientId = "dashboard_angular", ClientName = "Dashboard Angular App", AllowedGrantTypes = GrantTypes.Code, RequirePkce = true, RequireClientSecret = false, RedirectUris = { "https://your-angular-app-url/auth-callback" }, PostLogoutRedirectUris = { "https://your-angular-app-url/logout-callback" }, AllowedScopes = { "openid", "profile", "dashboard_api.read" }, AllowAccessTokensViaBrowser = true } }; }
Make sure to update the URLs, client secrets, and scopes to match your actual applications.
3. Secure Your APIs
Now, configure each API to validate tokens issued by your IdentityServer4 instance.
For .NET Core APIs (Program.cs/.NET 6+):
var builder = WebApplication.CreateBuilder(args); // Add authentication builder.Services.AddAuthentication("Bearer") .AddJwtBearer("Bearer", options => { options.Authority = "https://your-identityserver-url"; options.TokenValidationParameters = new TokenValidationParameters { ValidateAudience = true, ValidAudience = "customer_management_api" // Replace with the API's resource name }; }); // Add authorization builder.Services.AddAuthorization(); var app = builder.Build(); app.UseAuthentication(); app.UseAuthorization(); // Your API endpoints here app.MapControllers().RequireAuthorization(); app.Run();
Repeat this setup for each API, updating the ValidAudience to match the corresponding API resource name (e.g., human_resource_api for the HR API).
4. Client-Side Token Handling
Each client type needs to handle authentication and token retrieval differently:
- JavaScript/Angular SPAs: Use libraries like
oidc-client-js(JS) orangular-oauth2-oidc(Angular) to handle the OAuth2 flow. These libraries will handle redirects, token storage, and adding theAuthorization: Bearer {token}header to API requests. - iOS/Android Apps: Implement the Authorization Code Flow with PKCE. For iOS, you can use libraries like AppAuth-iOS; for Android, use AppAuth-Android. These handle the secure token exchange without storing client secrets.
- MVC Applications: Use the ASP.NET Core OpenID Connect middleware to handle authentication. The middleware will automatically acquire tokens and attach them to API requests (you can use
IHttpClientFactorywith an authenticated handler).
Final Checks
- Ensure all client redirect URIs are correctly registered in IdentityServer and match the app's actual callback URLs.
- Test each client to verify they can only access their assigned APIs (e.g., the Dashboard Angular app shouldn't be able to call the HR API).
- Use HTTPS for all endpoints (IdentityServer, APIs, and client apps) to secure token transmission.
内容的提问来源于stack exchange,提问作者barteloma

