Přeskočit na hlavní obsah

Identity Provider - Authentication, Authorization

Identity provider service authentication and Multitenancy and Multi-company authorization.

Prerequisites

Key endpoints

  • [GET] https://[HostName]/api/asol/idp/.well-known/openid-configuration
    • Authentication service discovery document endpoint. All metadata about authentication configuration. All information what you need for correct authenticate. It returns metadata information like the issuer name, key material, supported scopes etc.
  • [POST] https://[HostName]/api/asol/idp/connect/authorize
    • Interactive oauth2/oidc authentication flow. Usage when you need user to service authentication with user interaction to with consent, username, password, tokens etc.
  • [POST] https://[HostName]/api/asol/idp/connect/token
    • Non-interactive oauth2/oidc authentication flow. Usage when you need service to service authentication without user interaction.
  • [POST] https://[HostName]/api/asol/idp/account/login
    • Confirm user credentials to authenticate. After call https://[HostName]/api/asol/idp/connect/authorize and handshake oauth/oidc authorization code flow

Difference between user and clients (services) accounts

Authentication have two fundamentals: Human interactive authentication and non interactive client (service) authentication.

  • User - A user is a human that is using a registered client (service) to access resources. Typicaly usage for frontend authentication.
  • Client - A client (service) is a piece of software that requests tokens from Identity Provider. Typicaly usage for backend to backend authenticated communication.

Authenticate as Service (without user interaction)

private readonly IServiceProvider _serviceProvider;
private readonly SsoAuthOptions _authOptions;

//create access token using clientId and clientSecret (OIDC)
var authClient = new HttpClient { BaseAddress = _authBaseUri };

// - get discovery document for SSO configuration
var discoveryRequest = new DiscoveryDocumentRequest { Address = _authOptions.Authority };
var discoveryResponse = await authClient.GetDiscoveryDocumentAsync(discoveryRequest, cancellationToken: ct);
if (discoveryResponse.IsError)
{
throw new InvalidOperationException($"'{discoveryResponse.Error}' - '{discoveryResponse.ErrorType}'", discoveryResponse.Exception);
}

// - get access token using client credentials
var tokenRequest = new ClientCredentialsTokenRequest
{
Address = discoveryResponse.TokenEndpoint,
ClientId = _authOptions.ClientId,
// ClientSecret value must be ommited, or setup to null or empty value when client (_authOptions.ClientId) marked as public type (typicaly for authorization code flow, password flow).
// ClientSecret must be setup to non-null/non empty value for all others client types.
ClientSecret = _authOptions.ClientSecret,
Scope = _authOptions.Audience
};

// - append requested tenantId (optional)
if (!string.IsNullOrEmpty(tenantId))
{
tokenRequest.Parameters.Add("tid", tenantId);
}

var tokenResponse = await authClient.RequestClientCredentialsTokenAsync(tokenRequest, ct);
if (tokenResponse.IsError)
{
throw new InvalidOperationException($"'{tokenResponse.Error}' - '{tokenResponse.ErrorDescription}'", discoveryResponse.Exception);
}

var authHeader = new AuthenticationHeaderValue(tokenResponse.TokenType, tokenResponse.AccessToken);
// authHeader use as http Authorization header to any RestAPI endpoint to delegate identity as a service to any RestAPI endpoint.

Authenticate as User (with user interaction)

private readonly IServiceProvider _serviceProvider;
private readonly SsoAuthOptions _authOptions;

//create access token using username, userpassword, clientId and clientSecret (OIDC)
var authClient = new HttpClient { BaseAddress = _authBaseUri };

// - get discovery document for SSO configuration
var discoveryRequest = new DiscoveryDocumentRequest { Address = _authOptions.Authority };
var discoveryResponse = await authClient.GetDiscoveryDocumentAsync(discoveryRequest, cancellationToken: ct);
if (discoveryResponse.IsError)
{
throw new InvalidOperationException($"'{discoveryResponse.Error}' - '{discoveryResponse.ErrorType}'", discoveryResponse.Exception);
}

// Your own authorization flow with user interaction.

On angular project you can use npm package for doing this interactive flow: angular-oauth2-oidc

Authenticate with platform Core support (c#)

Backend prerequisites

Example of configuration for ASP.NET Core project which hosting your RestAPI:

"SsoAuthOptions": {
"Authority": "https://[HostName]/api/asol/idp",
"EnableCaching": true,
"CacheDuration": "00:01:00",
"RequireHttpsMetadata": false,
"Audience": "apiim",
"ClientId": "<<your application client unique identification>>",
"ClientSecret": "<<your application client secret>>"
},

Gets client token as a service access (without user context)

Dependency injection configuration:

services.AddApiConnectorInfrastructure();

How to get token everywhere you needs:

using Pathoschild.Http.Client;

class MyApiClientClass
{
public MyApiClientClass(IConnectorTokenProvider tokenProvider)
{
TokenProvider = tokenProvider;
}

protected IConnectorTokenProvider TokenProvider { get; }

public async Task CallOtherServiceExampleAsync(CancellationToken ct)
{
// create http client with fluent restAPI client wrapper (from Pathoschild nuget)
var client = new FluentClient("<<base url of target service>>");
// get access token for service configured with "SsoAuthOptions"
var authHeader = await TokenProvider.GetAuthenticationHeaderAsync(null, ct);

// prepare request
var request = Client.GetAsync("api/asol/idp/api/v1/usertenants", ct)
// add access token to http headers
.WithAuthentication(authHeader.Scheme, authHeader.Parameter);
// allow cancel request
.WithCancellationToken(ct);

// call target service RestAPI with access token and gets response as plain text string data.
var result = await request.AsString();
}
}

How to manual upload roles, roleauthorization and force update immediately

When something wrong (delayed) with roles and or roleauthorizations auto update, you can force update via API call.

Possible reasons why a new version of the data from roleauthorization.json is not uploaded:

  1. The definition contains an error. The IDM service then uses the previous version
  2. Someone uploaded the definitions via the API, or has their local computer directed to the queue in the development environment and uploads their version with every "second" start.
  3. The frontend overwrote the backend definition (they accidentally used the same feature code).
  4. IDM has the queue for uploading blocked by something else and therefore will not upload the BE definition right away, but later.
  5. API error in IDM, or error in the IDM cache
  6. Some development process replaced the DB after the service started that was supposed to upload its roleauthorization
  7. The service deployed on DEV does not contain the RoleAuthorizations.json definition, because it is a different version than the one currently in the develop branch

Prerequisities:

  • Service account
  • User account with TenantAdmin role.

Update Roles POST https://{stage}.avaplace.com/api/asol/idm/api/v1/Process/ImportBuiltInRoles/Upload
Update RoleAuthorization POST https://{stage}.avaplace.com/api/asol/idm/api/v1/Process/ImportBuiltInRoleAuthorizations/Upload

TIP: To call this upload API you can use Swagger or Postman.