DISCLAIMER I’m assuming you are familiar with OpenID Connect and IdentityServer, well in this post I’m not going to explain. So if you are not familiar, I recommend you to visit the official documnentation OpenID Connect and IdentityServer4

Introduction

Sometimes, when you work with IdentityServer, you need to add additional API endpoints to the application that host your IdentityServer. In our case, one of the bussines rules is to bring the posibility to the users to change theirs passwords from the mobile applications.

In a web application is pretty straightforward, you can redirect users to a web page to change theirs passwords because IdentityServer register one cookie for the authentication session, but in mobile applications we haven’t cookies. At the first time, I thought if could be possible to achieve this using the web view and cookies in order to reuse all the infrastructure that we have for web applications in our IdentityServer, but I couldn’t find a solution (I’d love to hear from you. Please leave comment below).

The next idea was to create and API endpoint in the application that host our IdentityServer, create a new client and configure the Microsoft’s JwtBearer handler in our IdentityServer to act as an API Resource, so we could pass the access token from the mobile application and change the password. It was a good idea but looking for IdentityServer 4 documentation I found this link

The guys who develop IdentityServer created some helpers to add more endpoints to IdentityServer and of course they give a warning:

You could achieve the same by using either our IdentityServerAuthentication handler or Microsoft’s JwtBearer handler. But this is not recommended since it requires more configuration and creates dependencies on external libraries that might lead to conflicts in future updates.

The API approach was good but how to configure the access not. So I decided to use these helpers.

Getting started

The first step is to resgister to API as an ApiResource:

await context.ApiResources.AddAsync(
    new ApiResource(
        IdentityServerConstants.LocalApi.ScopeName
    ).ToEntity());

IdentityServer bring us some predefined constants to use the local Api. In this case the value of IdentityServerConstants.LocalApi.ScopeName is IdentityServerApi

Next, we have to add this scope to our mobile client:

new Client()
{
    ClientId = "mobile",
    ClientName = "Mobile Apps",
    AllowedGrantTypes = GrantTypes.Hybrid,
    ClientSecrets = ...
    RedirectUris = { ... },
    PostLogoutRedirectUris = { ... },
    RequireConsent = false,
    RequirePkce = true,
    AllowedCorsOrigins = { ... },
    AllowedScopes = new List<string>
    {
        //Omitted for brevity
        IdentityServerConstants.LocalApi.ScopeName
    },
    AllowOfflineAccess = true,
    AllowAccessTokensViaBrowser = true
}

Next step is to enable the token validation for our local Api. In our IdentityServer’s Startup configuration we need to add the services:

services.AddLocalApiAuthentication()

To protect our action controller, decorate it with the Authorize attributte using the policy LocalApi.PolicyName:

[HttpPost]
[Authorize(LocalApi.PolicyName)]
[Route("api/account/changepassword")]
public async Task<IActionResult> ChangePasswordMobile([FromBody] ChangePasswordViewModel model)
{
    if (!ModelState.IsValid)
    {
        return new ObjectResult(new ValidationProblemDetails(ModelState))
        {
            ContentTypes = { "application/problem+json" },
            StatusCode = StatusCodes.Status400BadRequest
        };
    }

    var email = User.Claims.SingleOrDefault(c => c.Type == "email")?.Value;

    var user = await _userManager.FindByEmailAsync(email);

    var result = await _userManager.ChangePasswordAsync(
        user,
        model.CurrentPassword,
        model.Password);

    if (!result.Succeeded)
    {
        AddErrors(result);

        return new ObjectResult(
            new ValidationProblemDetails(ModelState))
            {
                ContentTypes = { "application/problem+json" },
                StatusCode = StatusCodes.Status400BadRequest
            };
    }

    return Ok();
}

So our mobile applications can request tokens and call to our API to change the password.

Under the hood, the method AddLocalApiAuthentication does some things:

  • Register an authentication handler to validate tokens using the IdentityServer infrastructure.

  • This authentication handler require a scope claim with the value of the constant IdentityServerConstants.LocalApi.ScopeName which value is IdentityServerApi.

  • Add a policy to check for a scope claim with the value of the constant LocalApi.PolicyName (IdentityServerApi)

If you want to use more advance scenarios, please visit this link

Conclusion

In this post I’ve tried to show you how we can add additional API Endpoints to the IdentityServer and protect them using tokens.