Skip to content

Authentication

moattarwork edited this page Dec 14, 2022 · 1 revision

LittleBlocks has built-in support for federated authentication using OAuth2. At the moment, It is only supporting AzureAD but the support for external providers will be added in the future.

The current implementation provides OAuth2 support as well as Impersonation capability which can be used in test environment when an automated or manual testing is needed with different access token (Indicating different users)

Authentication Mode

The authentication for the API can be configured in 3 modes:

  • None: which doesn't add any support for authentication to the API. Obviously, using AuthorizeAttribute with this mode will cause InternalServerError failure.
  • Impersonated: This mode is mainly for testing purpose and give the developers a way to call the api with an unsigned token. Adding the generated token using ImpersonationBearer scheme as "ImpersonationBearer token-value" as Authorization header will be enough to be identified by api as a proper ClaimsPrincipal in the context of the call. See Generate Impersonation Token section for more information.
  • OAuth2: This mode is a proper support of OAuth2 for Authentication. Currently the support for refresh token is not included in this release.

How to configure

Configuring the authentication can be done in two ways:

  • Using configuration
  • Using Fluent API

Using configuration

Adding the following section to the appsettings.json will automatically enable authentication for the API.

  "AuthOptions": {
    "AuthenticationMode":  "OAuth2", // None, Impersonated, OAuth2
    "Authentication": {
      "Authority": "https://login.microsoftonline.com/<AzureAD SubscriptionID ...>",
      "Audience": "ApplicationId comes here ..."
    }
  },

The Authentication section of AuthOptions is necessary only when the AuthenticationMode is OAuth2.

By adding this section to the configuration, the relative arrangement will be configured in the api so adding the Authorization header to the calls gives the successful access to underlying operations.

Using Fluent API

Fluent API gives the developers good facilities to setup the authentication modes during the bootstrapping of the API. It can be a replacement for the using configuration or even for overriding the configuration due to some runtime parameters.

The following code show how it can be configured in bootstrapping time:

public void ConfigureServices(IServiceCollection services)
{
    services.BootstrapApp<StartupForIntegration>(Configuration,
        app => app.AddConfigSection<Clients>()
            .AndSection<Section1>()
            .AndSection<Section2>()
            .HandleApplicationException<TemplateApiApplicationException>()
            .AndHandle<ThirdPartyPluginException>()
            .UseUserErrors()
            .ConfigureAuthentication(o => o.WithOAuth2().UseParameters("authority", "audience"))
            .AddServices((container, config) =>
            {
                container.TryAddTransient<IMyService, MyService>();
            })
    );
}

The method ConfigureAuthentication can be called with NoAuth and WithImpersonation modes which are configuring the authentication for other modes of operations.

API Documentation

Setting up the authentication in Impersonated or OAuth2 mode, automatically enable the behavior for Swagger UI in the API. Due to the AuthenticationMode swagger UI enables different way of authentication for the API:

  • Impersonated: In this mode, Clicking Authorize button in swagger UI displays a ApiKey interface which the combination of "ImpersonationBearer token-value" as helps user to call the APIs in Impersonated mode.
  • OAuth2: In this mode, Authorize button uses implicit flow mode of OAuth2 to authorize the access to the services.

NOTE

For setting up the implicit flow in identify provider, make sure the following configuration is in place:

  • oauth2AllowImplicitFlow has been set to true
  • Implicit Grant to Access Token has been set to true
  • Redirect Uris contains:

Authorization Header Propagation

Setting up the authentication in Impersonated or OAuth2 mode, every implementation of IRestClient automatically includes the Authorization Header for the outbound call. It can be disabled by explicitly defining this using ClientBuilder fluent configuration.

services.BootstrapApp<StartupForIntegration>(Configuration,
    app => app.AddConfigSection<Clients>()
        .AndSection<Section1>()
        .AndSection<Section2>()
        .HandleApplicationException<TemplateApiApplicationException>()
        .AndHandle<ThirdPartyPluginException>()
        .UseUserErrors()
        .ConfigureAuthentication(o => o.WithOAuth2().UseParameters("authority", "audience"))
        .AddServices((container, config) =>
        {
            // Register rest client with excluding AuthorizationHeader
            container.AddRestClient<IValuesClient, Clients>(c => c.ProducerClientUrl, option => option.ExcludeAuthorizationHeader());

            container.TryAddTransient<IMyService, MyService>();
        })
);

Generate Impersonation Token

Generating impersonation token is easy. The following code shows how it can be generated:

public class JwtTokenGenerator : ITokenGenerator
{
    private readonly string key = "401b09eab3c013d4ca54922bb802bec8fd5318192b0a75f201d8b3727429090fb337591abd3e444" +
                                    "53b954555b7a0812e1081c39b740293f765eae731f5a65ed1";

    public string GenerateToken()
    {
        var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key));
        var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256Signature);

        var header = new JwtHeader(credentials);

        var payload = new JwtPayload
        {
            {"identifier", "user identifier"},
            {"given_name", "first name"},
            {"family_name", "last name"},
            {"name", "display name"},
            {"email","email"},
            {"unique_name","email"},
            {"roles", "list of the user roles"},
            {"groups", "list of the user groups"},
            {"exp", GetUnixTime(DateTime.Today.AddMonths(1))},
        };

        var securityToken = new JwtSecurityToken(header, payload);
        var handler = new JwtSecurityTokenHandler();
        return handler.WriteToken(securityToken);
    }

    private int GetUnixTime(DateTime dt)
    {
        return (int)dt.Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
    }
Clone this wiki locally