Skip to content

Latest commit

 

History

History
96 lines (66 loc) · 5.26 KB

Authentication.md

File metadata and controls

96 lines (66 loc) · 5.26 KB

Authentication and Authorization

Beef leverages and integrates seamlessly with the ASP.NET Core out-of-the-box authentication and authorization capabilities.

The following sections describe the support enabled in each of the underlying solution projects.


Company.AppName.Api

The authentication process primarily takes place within the API itself. This capability is generally added within the Startup.cs leveraging the standard ASP.NET Core capabilities. The minimum requirement is the update of the ExecutionContext similar to the following within the Configure method.

For CoreEx the ExecutionContext plays a key role for housing the user details, namely the Username (optionally UserId). For the likes of authorization SetRoles can also be used. To enable additional capabilities a custom ExecutionContext can be created (inheriting base) similar to that demonstrated within the Cdr.Banking sample.

Note: in the following example, the Username is set to emails#oid claims as the emails value may not be unique and is mutable, whereas the oid is unique and immutable. This may be appropriate in your scenario especially where the Username is used for the likes of auditing.

public void Configure(IApplicationBuilder app)
{
    ...

    app.UseAuthentication();
    app.UseExecutionContext((ctx, ec) =>
    {
        //ec.UserName = ctx.User.Identity?.Name ?? "Anonymous";
        //return Task.CompletedTask;
        if (ctx.User.Identity is not null && ctx.User.Identity.IsAuthenticated)
        {
            ec.UserId = ctx.User.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier")?.Value;
            if (ec.UserId is null)
                throw new AuthenticationException("Token must have an 'oid' (object identifier) claim.");

            ec.UserName = $"{ctx.User.FindFirst("emails")?.Value ?? throw new AuthenticationException("Token must have an 'emails' laim.")}#{ec.UserId}";
        }
        else
            ec.UserName = "Anonymous";

        return Task.CompletedTask;
    });


    ...

Disclaimer: the above example is purely for illustrative purposes only; it is the responsibility of the developer to implement the appropriate authentication, claims verification, etc. that is applicable to their specific use case.


Company.AppName.CodeGen

For the authentication to occur within an API invocation the AuthorizeAttribute must be specified. The output of this attribute is controlled by the code generation configuration.

The following YAML elements support the webApiAuthorize attribute, generally specifying either of the two options, being Authorize or AllowAnonymous (the value is not validated so any supported value can be specified). The YAML value is inherited from its parent within the hierarchy where not explicitly defined (overridden).

  1. CodeGeneration
  2. Entity
  3. Operation

Note: Where no webApiAuthorize attribute is specified and cannot be inferred via parental inheritence, it will default to AllowAnonymous.


Company.AppName.Test

To support the intra-domain integration testing the bearer token may need to be passed from the test to the API otherwise all requests will fail with an authentication error.

The UnitTextEx.TestSetUp contains two delegates that can be provided to update the request to include credentials prior to send, these are OnBeforeHttpRequestSendAsync and OnBeforeHttpRequestMessageSendAsync depending on requirements. Where the test has been configured for a specified user (see WithUser), the username is passed as a parameter to the delegate. This should be configured as part of the one-time set up.

// Fixture set up.
TestSetUp.Default.OnBeforeHttpRequestMessageSendAsync = (req, userName, _) =>
{
    req.Headers.Add("cdr-user", userName);
    return Task.CompletedTask;
};

...

// Test logic with user name specified.
var v = Agent<AccountAgent, AccountCollectionResult>()
    .WithUser("jenny")
    .ExpectStatusCode(HttpStatusCode.OK)
    .Run(a => a.GetAccountsAsync(null)).Value;

Additionally, the authentication can be bypassed altogether, using the likes of the ApiTester.BypassAuthorization. This will result in the underlying services being configured with a BypassAuthorizationHandler.

ApiTester.BypassAuthorization();

// Invoke the agent.
Agent<FooAgent, int>()
    .ExpectStatusCode(HttpStatusCode.OK)
    .Run(a => a.BarAsync(1))
    .Assert(1234);