user1868744
user1868744

Reputation: 1033

IdentityServer4: get access token from Azure AD

I am using Azure AD as an external IdP with IdentityServer4. To call an API that is protected with AzureAd, I need to get access token from Azure Ad. Is it possible to get the access token as part of the login process and save it to claims?

I am using IdentityServer4 Quickstart UI. I tried to capture access token in the callback method of external token, but did not find that in the HttpContext or the claims or in the ProcessLoginCallbackForOidc method.

IdentityServer4 Azure Ad Configuration:

services.AddIdentityServer()
    .AddDeveloperSigningCredential()
    .AddInMemoryIdentityResources(Config.GetIdentityResources())
    .AddInMemoryApiResources(Config.GetApiResources())
    .AddInMemoryClients(Config.GetClients())
    .AddTestUsers(Config.GetUsers());

services.AddAuthentication()
    .AddOpenIdConnect("oidc", "Azure AD", options =>
    {
        options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
        options.SignOutScheme = IdentityServerConstants.SignoutScheme;

        options.Authority = "https://login.microsoftonline.com/fredhutch.onmicrosoft.com/";
        options.ClientId = "<client id>";
        options.Resource = "app_id from azure ad";
        options.ClientSecret = "secret from azure ad";
        options.ResponseType = "code id_token";
        options.TokenValidationParameters = new TokenValidationParameters
        {
            NameClaimType = "sub",
            RoleClaimType = "role"
        };

    });

Client configuration in IdentityServer4:

new Client
{
    ClientId = "mvc",
    ClientName = "MVC Client",
    ClientSecrets =
    {
        new Secret("secret".Sha256())
    },
    AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,

    RedirectUris = { "http://localhost:49341/signin-oidc" },
    PostLogoutRedirectUris = { "http://localhost:49341/signout-callback-oidc" },

    AllowedScopes = new List<string>
    {
        IdentityServerConstants.StandardScopes.OpenId,
        IdentityServerConstants.StandardScopes.Profile,
        "b03d4318-278d-40fc-b6b3-3cf47a0e6f4d"
    },
    AllowOfflineAccess=true
}

Client (ASP.Net Core MVC):

services.AddAuthentication(options =>
{
    options.DefaultScheme = "Cookies";
    options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
    options.SignInScheme = "Cookies";

    options.Authority = "idsrv4url";
    options.ClientId = "mvc";
    options.ClientSecret = "secret";

    options.SaveTokens = true;
    options.ResponseType = "code id_token";

    options.Scope.Clear();
    options.Scope.Add("openid");
    options.Scope.Add("profile");
    options.Scope.Add("b03d4318-278d-40fc-b6b3-3cf47a0e6f4d");
    options.Scope.Add("offline_access");

    options.GetClaimsFromUserInfoEndpoint = true;
    options.SaveTokens = true;

});

Upvotes: 5

Views: 3597

Answers (2)

Dean Goodman
Dean Goodman

Reputation: 983

I was able to get the Azure AD id_token to show up in the ExternalController.Callback() (and therefore ProcessLoginCallbackForOidc()) in the QuickStart UI template by adding the SaveTokens flag into the IdentityServer OIDC setup:

services.AddIdentityServer()
    .AddDeveloperSigningCredential()
    .AddInMemoryIdentityResources(Config.GetIdentityResources())
    .AddInMemoryApiResources(Config.GetApiResources())
    .AddInMemoryClients(Config.GetClients())
    .AddTestUsers(Config.GetUsers());

services.AddAuthentication()
    .AddOpenIdConnect("oidc", "Azure AD", options =>
    {
        options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
        options.SignOutScheme = IdentityServerConstants.SignoutScheme;

        options.Authority = "https://login.microsoftonline.com/fredhutch.onmicrosoft.com/";
        options.ClientId = "<client id>";
        options.Resource = "app_id from azure ad";
        options.ClientSecret = "secret from azure ad";
        options.ResponseType = "code id_token";
        options.TokenValidationParameters = new TokenValidationParameters
        {
            NameClaimType = "sub",
            RoleClaimType = "role"
        };
        options.SaveTokens = true;
    });

With that flag set, the following code will now retrieve the AAD id_token successfully:

//External OpenId Connect callback
public async Task<IActionResult> Callback()
{
    var result = await HttpContext.AuthenticateAsync(IdentityConstants.ExternalScheme);
    var id_token = result.Properties.GetTokenValue("id_token");
    ...
}

Upvotes: 1

Espen Medb&#248;
Espen Medb&#248;

Reputation: 2315

Your setup against Azure AD is an implicit flow, meaning you only get an authorization code and id token (based on your responsetype = "code id_token").

What you need to do is subscribe to the OnAuthorizationCodeReceived event and ask for access token here.

options.Events.OnAuthorizationCodeReceived= contex => {
    var authCode = contex.ProtocolMessage.Code;
    ...
    // Get token
    ...
};

You can find more info here https://learn.microsoft.com/en-us/azure/active-directory/develop/v1-protocols-oauth-code#use-the-authorization-code-to-request-an-access-token

Upvotes: 3

Related Questions