PowerServer Management APIs can be used to get and modify information about the license, database connection, transaction etc., therefore, access to PowerServer Management APIs are disabled by default, in order to protect these sensitive information. A 403 "Forbidden" error or 405 "Method Not Allowed" error will occur when the APIs are requested.
To enable the use of the APIs, go to UserExtensions project -> UserStartup.cs, uncomment the following script if it is commented or add the script if it does not exist:
app.UsePowerServerManagementAPI()
After the APIs are enabled, you can access the APIs now.
If you want to have finer grained control over the APIs, you can define the authentication and authorization service.
You can use the built-in authentication service or other third-party authentication service.
To use the built-in authentication service, you can follow instructions in Authentication, and then define the authorization logics in UserExtensions project -> Authentication -> AuthenticationExtensions.cs:
The following uses JWT as an example to show you how to define the authentication and authorization service.
Step 1: Enable the use of the APIs.
Go to UserExtensions project -> UserStartup.cs, uncomment the following script if it is commented or add the script if it does not exist).
app.UsePowerServerManagementAPI()
Step 2: Use the built-in JWT server to implement authenticate to the installable cloud app.
Without authentication, anyone can access your application and the APIs without restriction.
To ensure only verified users can access the PowerServer Web APIs and PowerServer Management APIs, you can follow instructions in Authentication > Using JWT to implement basic authentication.
Step 3: Add authorization on top of authentication.
Once the user is authenticated, you can determine only users with specific roles (instead of all users) are allowed to access the PowerServer Management API.
You can add a few lines of code to implement the policy-based authorization in the authentication template.
Take the JWT authentication template as an example.
a) Add a role to the user information in the JWT token.
For example: In the DefaultUserStore.cs file, you can specify the role for user Alice as "Normal" and the role for user Bob as "Admin". The JWT token generated in this way will carry the role information.
using System.Collections.Generic; using System.Linq; using System.Security.Claims; using IdentityModel; using Microsoft.Extensions.Logging; namespace UserExtensions { public class DefaultUserStore : IUserStore { private readonly ILogger _logger; private readonly List<UserModel> _users; public DefaultUserStore(ILogger<DefaultUserStore> logger) { _logger = logger; _users = new List<UserModel> { new UserModel { Username = "alice", Password = "alice", Claims = new[] { // Claims the "serverapi" scope new Claim(JwtClaimTypes.Scope, "serverapi"), new Claim(JwtClaimTypes.Name, "Alice Smith"), new Claim(JwtClaimTypes.GivenName, "Alice"), new Claim(JwtClaimTypes.FamilyName, "Smith"), new Claim(JwtClaimTypes.WebSite, "http://alice.com"), new Claim(JwtClaimTypes.Email, "AliceSmith@email.com"), new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean), new Claim(JwtClaimTypes.Role, "Normal") } }, new UserModel { Username = "bob", Password = "bob", Claims = new[] { // Claims the "serverapi" scope new Claim(JwtClaimTypes.Scope, "serverapi"), new Claim(JwtClaimTypes.Name, "Bob Smith"), new Claim(JwtClaimTypes.GivenName, "Bob"), new Claim(JwtClaimTypes.FamilyName, "Smith"), new Claim(JwtClaimTypes.WebSite, "http://bob.com"), new Claim(JwtClaimTypes.Email, "BobSmith@email.com"), new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean), new Claim(JwtClaimTypes.Role, "Admin") } } }; } public UserModel? ValidateCredentials(string username, string password) { // Logs in with the default user in the script // If you want to integrate an existing user management system (such as database), uses DatabaseUserStore instead var user = _users.FirstOrDefault(x => x.Username == username && x.Password == password); if (user != null) { // Logs the authentication-success information _logger.LogInformation($"User <{username}> logged in."); return user; } else { // Logs the authentication-failed information _logger.LogError($"Invalid login attempt."); return default; } } } }
b) Modify the authorization policy to check the user role in the JWT token.
For example: You can add the rule for PowerServerConstants.ManagementAPIAuthorizePolicy in AuthenticationExtensions.cs.
Add the code policy.RequireRole("Admin");
to ensure
that the PowerServer Management APIs can only be accessed by the user
whose role is "Admin". Other users will not be able to access these
APIs.
Note: You need to modify the code block under the comment "// Rewrite the PowerServer management API authorization policy". (as you want only users with specific roles to access the PowerServer Management APIs.)
You should NOT modify the code block under the comment "// Rewrite the default PowerServer authorization policy". (otherwise, users of all roles can still access the normal PowerServer Web APIs.)
using System;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Logging;
using Microsoft.IdentityModel.Tokens;
using PowerServer.Core;
namespace UserExtensions
{
public static class AuthenticationExtensions
{
// To be called in Startup.ConfigureServices, for adding services related with the authentication module
public static IServiceCollection AddPowerServerAuthentication(
this IServiceCollection services, IConfiguration configuration)
{
// Uses JWT Server as the built-in authentication server
// Injects the JWT request handling program, user storage and token handler
// Adds the user validator to validate the user login
// Adds the token generator
services.AddSingleton<TokenHandler>();
// Configures user data
// The script below injects a default implementation that directly reads user data from code for validation
services.AddSingleton<IUserStore, DefaultUserStore>();
// Uncomment the following script if you want to integrate an existing user management system (like a database)
// Note: You shall use DatabaseUserStore to implement the logics of getting users from the database
//services.AddSingleton<IUserStore, DatabaseUserStore>();
// Injects the JWT Server middleware into the request pipeline
services.AddTransient<IStartupFilter, AuthenticationMiddleware>();
// Adds the PowerServer authentication configuration
// see https://docs.microsoft.com/en-us/aspnet/core/security/authentication
services.AddAuthentication()
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
{
// Loads the authentication configuration from Authentication.json
// Loads the token issuer
var issuer = configuration["JwtSetting:Issuer"];
// Loads the token audience
var audience = configuration["JwtSetting:Audience"];
// Loads the token key
var securityKey = configuration["JwtSetting:SecurityKey"];
SymmetricSecurityKey? symmetricKey = null;
if (!String.IsNullOrWhiteSpace(securityKey))
{
symmetricKey = new SymmetricSecurityKey(Convert.FromBase64String(securityKey));
}
// Sets the token validation parameters
options.TokenValidationParameters = new TokenValidationParameters
{
// Sets the token issuer
ValidIssuer = issuer,
// Enables the validation of the token issuer
ValidateIssuer = true,
// Sets the token audience
ValidAudience = audience,
// Enables the validation of token audience
ValidateAudience = true,
// Sets the token key
IssuerSigningKey = symmetricKey,
// Enables the validation of token key
ValidateIssuerSigningKey = true,
// Sets the token expiration time
RequireExpirationTime = true,
// Enables the validation of token expiration time
ValidateLifetime = true,
};
options.IncludeErrorDetails = true;
// It is recommended to enable this option in the development environment, bt disable it in production for GDPR compliance
// see https://aka.ms/IdentityModel/PII
IdentityModelEventSource.ShowPII = true;
});
// Adds the PowerServer authorization configuration
// see https://docs.microsoft.com/en-us/aspnet/core/security/authorization/introduction
services.AddAuthorization(options =>
{
// Rewrite the default PowerServer authorization policy
options.AddPolicy(PowerServerConstants.DefaultAuthorizePolicy, policy =>
{
// Requires that the authenciation is successful
policy.RequireAuthenticatedUser();
// Requires that the JwtBearer method is used
policy.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme);
// Requires that access_token contains "serverapi" scope
policy.RequireClaim("scope", "serverapi");
// You can define additional authentication logics here
// ...
});
// Rewrite the PowerServer management API authorization policy
options.AddPolicy(PowerServerConstants.ManagementAPIAuthorizePolicy, policy =>
{
// Requires that the authenciation is successful
policy.RequireAuthenticatedUser();
// Requires that the JwtBearer method is used
policy.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme);
// Requires that access_token contains "serverapi" scope
policy.RequireClaim("scope", "serverapi");
// You can define additional authentication logics here
policy.RequireRole("Admin");
});
});
return services;
}
}
}