This tutorial walks you through the necessary steps to get a minimal IdentityServer up and running. For simplicity we will host IdentityServer and the client in the same web application - this is not a very realistic scenario, but lets you get started without making it too complicated.

The full source code can be found here.

Part 1 - MVC Authentication & Authorization

In the first part we will create a simple MVC application and add authentication via IdentityServer to it. Then we will have a closer look at claims, claims transformation and authorization

Create the web application

In Visual Studio 2013, create a standard MVC application and set authentication to “No authentication”.

create mvc app

You can switch the project now to SSL using the properties window:

set ssl

Important Don’t forget to update the start URL in your project properties.

Adding IdentityServer

IdentityServer is based on OWIN/Katana and distributed as a Nuget package. To add it to the newly created web host, install the following two packages:

install-package Microsoft.Owin.Host.Systemweb
install-package IdentityServer3

Configuring IdentityServer - Clients

IdentityServer needs some information about the clients it is going to support, this can be simply supplied using a Client object:

public static class Clients
{
    public static IEnumerable<Client> Get()
    {
        return new[]
        {
            new Client 
            {
                Enabled = true,
                ClientName = "MVC Client",
                ClientId = "mvc",
                Flow = Flows.Implicit,

                RedirectUris = new List<string>
                {
                    "https://localhost:44319/"
                },
                
                AllowAccessToAllScopes = true
            }
        };
    }
}

Remark Right now the client has access to all scopes (via the AllowAccessToAllScopes setting). For production applications you would lock that down. More on that later.

Configuring IdentityServer - Users

Next we will add some users to IdentityServer - again this can be accomplished by providing a simple C# class. You can retrieve user information from any data store and we provide out of the box support for ASP.NET Identity and MembershipReboot.

public static class Users
{
    public static List<InMemoryUser> Get()
    {
        return new List<InMemoryUser>
        {
            new InMemoryUser
            {
                Username = "bob",
                Password = "secret",
                Subject = "1",

                Claims = new[]
                {
                    new Claim(Constants.ClaimTypes.GivenName, "Bob"),
                    new Claim(Constants.ClaimTypes.FamilyName, "Smith")
                }
            }
        };
    }
}

Adding Startup

IdentityServer is configured in the startup class. Here we provide information about the clients, users, scopes, the signing certificate and some other configuration options. In production you should load the signing certificate from the Windows certificate store or some other secured source. In this sample we simply added it to the project as a file (you can download a test certificate from here. Add it to the project and set its build action to Copy to output.

For info on how to load the certificate from Azure WebSites see here.

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.Map("/identity", idsrvApp =>
            {
                idsrvApp.UseIdentityServer(new IdentityServerOptions
                {
                    SiteName = "Embedded IdentityServer",
                    SigningCertificate = LoadCertificate(),

                    Factory = new IdentityServerServiceFactory()
                                .UseInMemoryUsers(Users.Get())
                                .UseInMemoryClients(Clients.Get())
                                .UseInMemoryScopes(StandardScopes.All)
                });
            });
    }

    X509Certificate2 LoadCertificate()
    {
        return new X509Certificate2(
            string.Format(@"{0}\bin\identityServer\idsrv3test.pfx", AppDomain.CurrentDomain.BaseDirectory), "idsrv3test");
    }
}

At this point you have a fully functional IdentityServer and you can browse to the discovery endpoint to inspect the configuration:

disco

RAMMFAR

One last thing, please don’t forget to add RAMMFAR to your web.config, otherwise some of our embedded assets will not be loaded correctly by IIS:

<system.webServer>
  <modules runAllManagedModulesForAllRequests="true" />
</system.webServer>

Adding and configuring the OpenID Connect authentication middleware

To add OIDC authentication to the MVC application, we need to add two packages:

install-package Microsoft.Owin.Security.Cookies
install-package Microsoft.Owin.Security.OpenIdConnect

Configure the cookie middleware in StartUp.cs with its default values:

app.UseCookieAuthentication(new CookieAuthenticationOptions
    {
        AuthenticationType = "Cookies"
    });

Point the OpenID Connect middleware (also in Startup.cs) to our embedded version of IdentityServer and use the previously configured client configuration:

app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
    {
        Authority = "https://localhost:44319/identity",
        ClientId = "mvc",
        RedirectUri = "https://localhost:44319/",
        ResponseType = "id_token",

        SignInAsAuthenticationType = "Cookies"
    });

Adding a protected resource and showing claims

To initiate the authentication with IdentityServer you need to create a protected resource, e.g. by adding a global authorization filter. For our sample we will simply protect the About action on the Home controller. In addition we will hand over the claims to the view so we can see which claims got emitted by IdentityServer:

[Authorize]
public ActionResult About()
{
    return View((User as ClaimsPrincipal).Claims);
}

The corresponding view looks like this:

@model IEnumerable<System.Security.Claims.Claim>

<dl>
    @foreach (var claim in Model)
    {
        <dt>@claim.Type</dt>
        <dd>@claim.Value</dd>
    }
</dl>

Authentication and claims

Clicking on the about link will now trigger the authentication. IdentityServer will show the login screen and send a token back to the main application. The OpenID Connect middleware validates the token, extracts the claims and passes them on to the cookie middleware, which will in turn set the authentication cookie. The user is now signed in.

login

claims

Adding role claims and scope

In the next step we want to add some role claims to our user which we will use later on for authorization.

For now we got away with the OIDC standard scopes - let’s define a roles scope that includes the role claim and add that to the standard scopes:

public static class Scopes
{
    public static IEnumerable<Scope> Get()
    {
        var scopes = new List<Scope>
        {
            new Scope
            {
                Enabled = true,
                Name = "roles",
                Type = ScopeType.Identity,
                Claims = new List<ScopeClaim>
                {
                    new ScopeClaim("role")
                }
            }
        };

        scopes.AddRange(StandardScopes.All);

        return scopes;
    }
}

Also change the factory in Startup to use the new Scopes:

Factory = new IdentityServerServiceFactory()
    .UseInMemoryUsers(Users.Get())
    .UseInMemoryClients(Clients.Get())
    .UseInMemoryScopes(Scopes.Get()),

Next we add a couple of role claims to Bob:

public static class Users
{
    public static IEnumerable<InMemoryUser> Get()
    {
        return new[]
        {
            new InMemoryUser
            {
                Username = "bob",
                Password = "secret",
                Subject = "1",

                Claims = new[]
                {
                    new Claim(Constants.ClaimTypes.GivenName, "Bob"),
                    new Claim(Constants.ClaimTypes.FamilyName, "Smith"),
                    new Claim(Constants.ClaimTypes.Role, "Geek"),
                    new Claim(Constants.ClaimTypes.Role, "Foo")
                }
            }
        };
    }
}

Changing the middleware configuration to ask for roles

By default the OIDC middleware asks for two scopes: openid and profile - this is why IdentityServer includes the subject and name claims. Now we add a request to the roles scope:

app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
    {
        Authority = "https://localhost:44319/identity",
                    
        ClientId = "mvc",
        Scope = "openid profile roles",
        RedirectUri = "https://localhost:44319/",
        ResponseType = "id_token",

        SignInAsAuthenticationType = "Cookies"
    });

After successful authentication, you should now see the role claims in the user’s claims collection:

role claims

Claims transformation

When you inspect the claims on the about page, you will notice two things: some claims have odd long type names and there are more claims than you probably need in your application.

The long claim names come from Microsoft’s JWT handler trying to map some claim types to .NET’s ClaimTypes class types. You can turn off this behavior with the following line of code (in Startup).

This also means that you need to adjust the configuration for anti-CSRF protection to the new unique sub claim type:

AntiForgeryConfig.UniqueClaimTypeIdentifier = Constants.ClaimTypes.Subject;
JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();

The claims will now look like this:

shorter claims

This is an improvement, but there are still some low level protocol claims that are certainly not needed by typical business logic. The process of turning raw incoming claims into application specific claims is called claims transformation. During this process you take the incoming claims, decide which claims you want to keep and maybe need to contact additional data stores to retrieve more claims that are required by the application.

The OIDC middleware has a notification that you can use to do claims transformation - the resulting claims will be stored in the cookie:

app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
    {
        Authority = "https://localhost:44319/identity",
                    
        ClientId = "mvc",
        Scope = "openid profile roles",
        RedirectUri = "https://localhost:44319/",
        ResponseType = "id_token",

        SignInAsAuthenticationType = "Cookies",
        UseTokenLifetime = false,

        Notifications = new OpenIdConnectAuthenticationNotifications
        {
            SecurityTokenValidated = n =>
                {
                    var id = n.AuthenticationTicket.Identity;

                    // we want to keep first name, last name, subject and roles
                    var givenName = id.FindFirst(Constants.ClaimTypes.GivenName);
                    var familyName = id.FindFirst(Constants.ClaimTypes.FamilyName);
                    var sub = id.FindFirst(Constants.ClaimTypes.Subject);
                    var roles = id.FindAll(Constants.ClaimTypes.Role);

                    // create new identity and set name and role claim type
                    var nid = new ClaimsIdentity(
                        id.AuthenticationType,
                        Constants.ClaimTypes.GivenName,
                        Constants.ClaimTypes.Role);

                    nid.AddClaim(givenName);
                    nid.AddClaim(familyName);
                    nid.AddClaim(sub);
                    nid.AddClaims(roles);

                    // add some other app specific claim
                    nid.AddClaim(new Claim("app_specific", "some data"));                   

                    n.AuthenticationTicket = new AuthenticationTicket(
                        nid,
                        n.AuthenticationTicket.Properties);
                    
                    return Task.FromResult(0);    
                }
        }
    });

After adding the above code, our claim set now looks like this: transformed claims

Authorization

Now that we have authentication and some claims, we can start adding simple authorization rules.

MVC has a built-in attribute called [Authorize] to require authenticated users, you could also use this attribute to annotate role membership requirements. We don’t recommend this approach because this typically leads to code that mixes concerns like business/controller logic and authorization policy. We rather recommend separating the authorization logic from the controller which leads to cleaner code and better testability (read more here).

Resource Authorization

To add the new authorization infrastructure and the new attribute, we add a Nuget package:

install-package Thinktecture.IdentityModel.Owin.ResourceAuthorization.Mvc

Next we annotate the Contact action on the Home controller with an attribute that expresses that executing that action is going to Read the ContactDetails resource:

[ResourceAuthorize("Read", "ContactDetails")]
public ActionResult Contact()
{
    ViewBag.Message = "Your contact page.";

    return View();
}

Note that the attribute is not expressing who is allowed to read the contacts - we separate that logic into an authorization manager that knows about actions, resources and who is allowed to do which operation in your application:

public class AuthorizationManager : ResourceAuthorizationManager
{
    public override Task<bool> CheckAccessAsync(ResourceAuthorizationContext context)
    {
        switch (context.Resource.First().Value)
        {
            case "ContactDetails":
                return AuthorizeContactDetails(context);
            default:
                return Nok();
        }
    }

    private Task<bool> AuthorizeContactDetails(ResourceAuthorizationContext context)
    {
        switch (context.Action.First().Value)
        {
            case "Read":
                return Eval(context.Principal.HasClaim("role", "Geek"));
            case "Write":
                return Eval(context.Principal.HasClaim("role", "Operator"));
            default:
                return Nok();
        }
    }
}

And finally we wire up the authorization manager into the OWIN pipeline in Startup:

app.UseResourceAuthorization(new AuthorizationManager());

Run the sample and step through the code to familiarize yourself with the flow.

Role Authorization

However, if you do choose to use [Authorize(Roles = "Foo,Bar")] be aware that sites can be thrown into an infinite redirection loop when the current user is authenticated, but does not belong to one of the roles or users you pass into the Authorize attribute (verified in MVC 5.2). This undesirable outcome occurs because the Authorize attribute will set the action’s result to 401 unauthorized when the user is authenticated, but not in one of the roles. That 401 result triggers a redirect to authenticate with IdentityServer, which authenticates and then redirects the user back, and then the redirect loop begins. This behavior can be overcome by overriding the Authorize attribute’s HandleUnauthorizedRequest method as follows, and then use the customized authorization attribute instead of what comes with MVC.

// Customized authorization attribute:
public class AuthAttribute : AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        if (filterContext.HttpContext.User.Identity.IsAuthenticated)
        {
            // 403 we know who you are, but you haven't been granted access
            filterContext.Result = new HttpStatusCodeResult(System.Net.HttpStatusCode.Forbidden);
        }
        else
        {
            // 401 who are you? go login and then try again
            filterContext.Result = new HttpUnauthorizedResult();
        }
    }
}

// Usage:
[Auth(Roles = "Geek")]
public ActionResult About()
{
    // ...
}

More authorization and dealing with access denied scenarios

Let’s do a bit more authorization by adding a new action method to the Home controller:

[ResourceAuthorize("Write", "ContactDetails")]
public ActionResult UpdateContact()
{
    ViewBag.Message = "Update your contact details!";

    return View();
}

When you try to invoke that action by navigating to the /home/updatecontact URL you will see a forbidden error page.

iis forbidden

In fact you will see different response based on the fact if the user is already authenticated or not. If not MVC will redirect to the login page, if authenticated, you will see the forbidden response. This is by design (read more here).

You can handle the forbidden condition by checking for 403 status codes - we provide such a filter out of the box:

[ResourceAuthorize("Write", "ContactDetails")]
[HandleForbidden]
public ActionResult UpdateContact()
{
    ViewBag.Message = "Update your contact details!";

    return View();
}

The HandleForbidden filter (which can also be global of course) will redirect to a specified view whenever a 403 got emitted - by default we look for a view called Forbidden.

forbidden

You can also use the authorization manager imperatively, which gives you even more options:

[HandleForbidden]
public ActionResult UpdateContact()
{
    if (!HttpContext.CheckAccess("Write", "ContactDetails", "some more data"))
    {
        // either 401 or 403 based on authentication state
        return this.AccessDenied();
    }

    ViewBag.Message = "Update your contact details!";
    return View();
}

Adding Logout

Adding logout is easy, simply add a new action that calls the Signout method in the Katana authentication manager:

public ActionResult Logout()
{
    Request.GetOwinContext().Authentication.SignOut();
    return Redirect("/");
}

This will initiate a roundtrip to the so called endsession endpoint on IdentityServer. This endpoint will clear the authentication cookie and terminate your session:

simple logout

Typically the most secure thing to do now would be to simply close the browser window to get rid of all session data. Some applications though would like to give the user a chance to return as an anonymous user.

This is possible, but requires some steps - first you need to register a valid URL to return to after the logout procedure is complete. This is done in the client definition for the MVC application (note the new PostLogoutRedirectUris setting):

new Client 
{
    Enabled = true,
    ClientName = "MVC Client",
    ClientId = "mvc",
    Flow = Flows.Implicit,

    RedirectUris = new List<string>
    {
        "https://localhost:44319/"
    },
    PostLogoutRedirectUris = new List<string>
    {
        "https://localhost:44319/"
    }
}

Next the client has to prove its identity to the logout endpoint to make sure we redirect to the right URL (and not some spammer/phishing page). This is done by sending the initial identity token back that the client received during the authentication process. So far we have discarded this token, now it’s the time to change the claims transformation logic to preserve it.

This is accomplished by adding this line of code to our SecurityTokenValidated notification:

// keep the id_token for logout
nid.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));

And as a last step, we have to attach the id_token when the user logs out and we make the roundtrip to IdentityServer. This is also done using a notification on the OIDC middleware:

RedirectToIdentityProvider = n =>
    {
        if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.LogoutRequest)
        {
            var idTokenHint = n.OwinContext.Authentication.User.FindFirst("id_token");

            if (idTokenHint != null)
            {
                n.ProtocolMessage.IdTokenHint = idTokenHint.Value;
            }
        }

        return Task.FromResult(0);
    }

With these changes, IdentityServer will give the user a link back to the calling application:

logout with redirect

Tip On the IdentityServerOptions you can find an AuthenticationOptions object. This has a property called EnablePostSignOutAutoRedirect. As you probably would expect, setting this to true will automatically redirect back to the client after logout.

Adding Google Authentication

Next we want to enable external authentication. This is done by adding additional Katana authentication middleware to IdentityServer - for our example we will use Google.

Registering IdentityServer with Google

First of all we need to register IdentityServer at Google’s developer console. This consists of a few steps.

First navigate to:

https://console.developers.google.com

Create a new Project

googlecreateproject

Enable the Google+ API

googleapis

Configure the consent screen with email address and product name

googleconfigureconsent

Create a client application

googlecreateclient

After you create the client application, the developer console will show you a client id and a client secret. We will need these two values later when we configure the Google middleware.

Adding the Google authentication middleware

Add the middleware by installing the following package:

install-package Microsoft.Owin.Security.Google

Configure the middleware

Add the following method to your Startup:

private void ConfigureIdentityProviders(IAppBuilder app, string signInAsType)
{
    app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions
        {
            AuthenticationType = "Google",
            Caption = "Sign-in with Google",
            SignInAsAuthenticationType = signInAsType,

            ClientId = "...",
            ClientSecret = "..."
        });
}

Next we point our IdentityServer options class to this method:

idsrvApp.UseIdentityServer(new IdentityServerOptions
{
    SiteName = "Embedded IdentityServer",
    SigningCertificate = LoadCertificate(),

    Factory = new IdentityServerServiceFactory()
        .UseInMemoryUsers(Users.Get())
        .UseInMemoryClients(Clients.Get())
        .UseInMemoryScopes(Scopes.Get()),

    AuthenticationOptions = new IdentityServer3.Core.Configuration.AuthenticationOptions
    {
        IdentityProviders = ConfigureIdentityProviders
    }
});

That’s it! The next time the user logs in - there will be a “Sign-in with Google” button on the login page:

googlesignin

Notice that the role claim is missing when signing-in with Google. That makes sense since Google does not have the concept of roles. Be prepared that not all identity providers will offer the same claim types.

Part 2 - Adding and calling a Web API

In this part we’ll be adding a Web API to the solution. The API will be secured by IdentityServer. Next our MVC application will call the API using both the trust subsystem and identity delegation approach.

Adding the Web API Project

The easiest way to create a clean API project is by adding an empty web project.

add empty api

Next we’ll add Web API and Katana hosting using Nuget:

install-package Microsoft.Owin.Host.SystemWeb
install-package Microsoft.Aspnet.WebApi.Owin

Adding a Test Controller

The following controller will return all claims back to the caller - this will allow us to inspect the token that will get sent to the API.

[Route("identity")]
[Authorize]
public class IdentityController : ApiController
{
    public IHttpActionResult Get()
    {
        var user = User as ClaimsPrincipal;
        var claims = from c in user.Claims
                        select new
                        {
                            type = c.Type,
                            value = c.Value
                        };

        return Json(claims);
    }
}

Wiring up Web API and Security in Startup

As always with Katana-based hosting, all configuration takes place in Startup:

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        // web api configuration
        var config = new HttpConfiguration();
        config.MapHttpAttributeRoutes();

        app.UseWebApi(config);
    }
}

In addition we want to secure our API using IdentityServer - two things are needed for that:

To accomplish that, we add a Nuget packages:

install-package IdentityServer3.AccessTokenValidation

..and use them in Startup:

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
        {
            Authority = "https://localhost:44319/identity",
            RequiredScopes = new[] { "sampleApi" }
        });
        
        // web api configuration
        var config = new HttpConfiguration();
        config.MapHttpAttributeRoutes();

        app.UseWebApi(config);
    }
}

Note IdentityServer issues standard JSON Web Tokens (JWT), and you could use the plain Katana JWT middleware to validate them. Our middleware is just a convenience since it can auto-configure itself using the IdentityServer discovery document (metadata).

Registering the API in IdentityServer

Next we need to register the API - this is done by extending the scopes. This time we add a so called resource scope:

public static class Scopes
{
    public static IEnumerable<Scope> Get()
    {
        var scopes = new List<Scope>
        {
            new Scope
            {
                Enabled = true,
                Name = "roles",
                Type = ScopeType.Identity,
                Claims = new List<ScopeClaim>
                {
                    new ScopeClaim("role")
                }
            },
            new Scope
            {
                Enabled = true,
                DisplayName = "Sample API",
                Name = "sampleApi",
                Description = "Access to a sample API",
                Type = ScopeType.Resource
            }
        };

        scopes.AddRange(StandardScopes.All);

        return scopes;
    }
}

Registering a Web API Client

Next we will call the API. You can do that either as using client credentials (think service account) or by delegating the users identity. We will start with the client credentials.

First we need to register a new client for the MVC app. For security reasons, IdentityServer only allows one flow per client, and since our existing MVC client already uses implicit flow, we need to create a new client for the service to service communication.

public static class Clients
{
    public static IEnumerable<Client> Get()
    {
        return new[]
        {
            new Client 
            {
                ClientName = "MVC Client",
                ClientId = "mvc",
                Flow = Flows.Implicit,

                RedirectUris = new List<string>
                {
                    "https://localhost:44319/"
                },
                PostLogoutRedirectUris = new List<string>
                {
                    "https://localhost:44319/"
                },
                AllowedScopes = new List<string>
                {
                    "openid",
                    "profile",
                    "roles",
                    "sampleApi"
                }
            },
            new Client
            {
                ClientName = "MVC Client (service communication)",   
                ClientId = "mvc_service",
                Flow = Flows.ClientCredentials,

                ClientSecrets = new List<Secret>
                {
                    new Secret("secret".Sha256())
                },
                AllowedScopes = new List<string>
                {
                    "sampleApi"
                }
            }
        };
    }
}

Remark The above snippet locks down the scopes that can be accessed by the various clients using the AllowedScopes setting.

Calling the API

Calling the API consists of two parts:

To make the interaction with the OAuth2 token endpoint easier, add the Client package to the MVC project via Nuget:

install-package IdentityModel

Under Controller, add the new class CallApiController. The following code snippet requests the token for sampleApi using the client credentials:

private async Task<TokenResponse> GetTokenAsync()
{
    var client = new TokenClient(
        "https://localhost:44319/identity/connect/token",
        "mvc_service",
        "secret");

    return await client.RequestClientCredentialsAsync("sampleApi");
}

Whereas the following snippet calls our identity endpoint using the requested access token:

private async Task<string> CallApi(string token)
{
    var client = new HttpClient();
    client.SetBearerToken(token);

    var json = await client.GetStringAsync("https://localhost:44321/identity");
    return JArray.Parse(json).ToString();
}

Bringing that all together, a newly added controller calls the service and displays the resulting claims on a view:

public class CallApiController : Controller
{
    // GET: CallApi/ClientCredentials
    public async Task<ActionResult> ClientCredentials()
    {
        var response = await GetTokenAsync();
        var result = await CallApi(response.AccessToken);

        ViewBag.Json = result;
        return View("ShowApiResult");
    }

    // helpers omitted
}

Create ShowApiResult.cshtml file, simple view to see results:

<h2>Result</h2>

<pre>@ViewBag.Json</pre>

The result will look like this:

callapiclientcreds

In other words - the API knows about the caller:

All claims in the token will be turned into a ClaimsPrincipal and are available via the .User property on the controller.

Calling the API on behalf of the User

Next we want to call the API using the user’s identity. This is accomplished by adding the sampleApi scope to the list of scopes in the OpenID Connect middleware configuration. We also need to indicate that we want to request an access token by changing the response type:

Scope = "openid profile roles sampleApi",
ResponseType = "id_token token"

As soon as a response type of token is requested, IdentityServer stops including the claims in the identity token. This is for optimization purposes, since you now have an access token that allows retrieving the claims from the userinfo endpoint and while keeping the identity token small.

Accessing the userinfo endpoint is not hard - the UserInfoClient class can make this even simpler. In addition we also now store the access token in the cookie, so we can use it whenever we want to access the API on behalf of the user:

SecurityTokenValidated = async n =>
    {
        var nid = new ClaimsIdentity(
            n.AuthenticationTicket.Identity.AuthenticationType,
            Constants.ClaimTypes.GivenName,
            Constants.ClaimTypes.Role);

        // get userinfo data
        var userInfoClient = new UserInfoClient(
            new Uri(n.Options.Authority + "/connect/userinfo"),
            n.ProtocolMessage.AccessToken);

        var userInfo = await userInfoClient.GetAsync();
        userInfo.Claims.ToList().ForEach(ui => nid.AddClaim(new Claim(ui.Item1, ui.Item2)));

        // keep the id_token for logout
        nid.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));

        // add access token for sample API
        nid.AddClaim(new Claim("access_token", n.ProtocolMessage.AccessToken));

        // keep track of access token expiration
        nid.AddClaim(new Claim("expires_at", DateTimeOffset.Now.AddSeconds(int.Parse(n.ProtocolMessage.ExpiresIn)).ToString()));

        // add some other app specific claim
        nid.AddClaim(new Claim("app_specific", "some data"));

        n.AuthenticationTicket = new AuthenticationTicket(
            nid,
            n.AuthenticationTicket.Properties);
    }

Another option would be to reconfigure the scopes in IdentityServer and set the AlwaysIncludeInIdToken flag on the scope claims to force inclusion of the claims in the identity token - I’ll leave that as an exercise for the reader.

Calling the API

Since the access token is now stored in the cookie, we can simply retrieve it from the claims principal and use it to call the service:

// GET: CallApi/UserCredentials
public async Task<ActionResult> UserCredentials()
{
    var user = User as ClaimsPrincipal;
    var token = user.FindFirst("access_token").Value;
    var result = await CallApi(token);

    ViewBag.Json = result;
    return View("ShowApiResult");
}

After having signed in you can now see on the result page that the sub claim is included, which means that the API is now working on behalf of a user:

userdelegation

If you now add a scope claim for role to the sampleApi scope - the roles of the user will be included in the access token as well:

new Scope
{
    Enabled = true,
    DisplayName = "Sample API",
    Name = "sampleApi",
    Description = "Access to a sample API",
    Type = ScopeType.Resource,

    Claims = new List<ScopeClaim>
    {
        new ScopeClaim("role")
    }
}

delegationroles