how to define AuthorizationCodeTokenRequest with ClientAssertion

2.2k Views Asked by At

i'm trying to figure out how the define my AuthorizationCodeTokenRequest for Code Flow for the JWT scenario , using the IdentityModole freamwork

let say i have a defined client on my OP Server


new Client
{
ClientId = "myClientId"
ClientSecrets = {
new Secret("MyVerySpecialSecret".Sha256())
}

on the client side i would like to get an AuthorizationCode using JWT

        var securityToken = tokenHandler.CreateJwtSecurityToken(
            issuer: clientID,
            audience: opEndPoint.TokenEndpoint,
            subject: new ClaimsIdentity(new List<Claim>()
            {
                        new Claim(JwtClaimTypes.JwtId, Guid.NewGuid().ToString()),
                        new Claim(JwtClaimTypes.Subject, clientID),
                        new Claim(JwtClaimTypes.IssuedAt, new DateTimeOffset(now).ToEpochTime().ToString(),
                            ClaimValueTypes.Integer64)
            }),
            expires:now.AddMinutes(5),
            signingCredentials: new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes("MyVerySpecialSecret")), SecurityAlgorithms.HmacSha256Signature)
        );

        var clientAuthJwt = tokenHandler.WriteToken(securityToken);
var request = new AuthorizationCodeTokenRequest()
{
Address = opEndPoint.TokenEndpoint,
ClientId = clientID,
Code = code,
ClientAssertion = new ClientAssertion()
{
Type = OidcConstants.ClientAssertionTypes.JwtBearer,
Value = clientAuthJwt
},
RedirectUri = opEndPoint.RedirectUri,
GrantType = OidcConstants.GrantTypes.AuthorizationCode
};

    var response = client.RequestAuthorizationCodeTokenAsync(request).Result;

i'm getting "invalid_client" , so clearly the SigningCredentials i am using is not correct could not find anywhere a working code example.

1

There are 1 best solutions below

4
On

First of all you need to define in the Client Definition in IdentityServer what flows you want the client to support.

You do this by setting the following property:

AllowedGrantTypes = GrantTypes.Code,

See the details here for how to properly create a client definition.

A sample client can look like this:

       var client2 = new Client
        {
            ClientId = "authcodeflowclient",        //Unique ID of the client
            ClientName = "AuthCodeFlow Client",     //Client display name (used for logging and consent screen)
            ClientUri = "https://www.edument.se",   //URI to further information about client (used on consent screen)
            RequirePkce = true,
            AllowOfflineAccess =true,

            ClientSecrets = new List<Secret>
            {
                new Secret
                {
                    Value = "mysecret".Sha512()
                }
            },

            AllowedGrantTypes = GrantTypes.Code,

            // When requesting both an id token and access token, should the user claims always
            // be added to the id token instead of requiring the client to use the userinfo endpoint.
            // Defaults to false.
            AlwaysIncludeUserClaimsInIdToken = false,

            //Specifies whether this client is allowed to receive access tokens via the browser. 
            //This is useful to harden flows that allow multiple response types 
            //(e.g. by disallowing a hybrid flow client that is supposed to  use code id_token to add the token response type and thus leaking the token to the browser.
            AllowAccessTokensViaBrowser = false,

            RedirectUris =
            {
                "https://localhost:5001/CodeFlow/Callback",
                "https://localhost:5001/RefreshToken/Callback",
                "https://localhost:5001/signin-oidc"
            },

            PostLogoutRedirectUris = { "https://localhost:5001/signout-callback-oidc" },

            // By default a client has no access to any resources
            // specify the allowed resources by adding the corresponding scopes names.
            // If empty, the client can't access any scope
            AllowedScopes =
            {
                //Standard scopes
                IdentityServerConstants.StandardScopes.OpenId,
                IdentityServerConstants.StandardScopes.Email,
                IdentityServerConstants.StandardScopes.Profile,
                IdentityServerConstants.StandardScopes.Phone,
                "employee_info",
                "shop.admin",
                "shop.employee",
                "shop.guest"
            },

            AllowedCorsOrigins =
            {
                "https://localhost:5001"
            }
        };

SigningCredentials is a separate thing that controls how the keys issued by IdentityServer are issued. In development you don't need to worry about that because its all taken care of when you call this method . builder.AddDeveloperSigningCredential();

You only need to deal with SigningCredentials when you deploy IdentityServer.

Client side, you need in your ASP.NET Core client application write code that looks like this:

        services.AddAuthentication(options =>
        {
            options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
        }).AddCookie(options =>
        {
            options.LoginPath = "/User/Login";
            options.LogoutPath = "/User/Logout";
            options.AccessDeniedPath = "/User/AccessDenied";
        }).AddOpenIdConnect(options =>
        {
            options.Authority = "https://localhost:6001";
            options.ClientId = "authcodeflowclient";
            options.ClientSecret = "mysecret";
            options.ResponseType = "code";

            options.Scope.Clear();
            options.Scope.Add("openid");
            options.Scope.Add("profile");
            options.Scope.Add("email");
            options.Scope.Add("employee_info");



            options.SaveTokens = true;
            options.SignedOutRedirectUri = "/";
            options.GetClaimsFromUserInfoEndpoint = true;

            options.TokenValidationParameters = new TokenValidationParameters
            {
                NameClaimType = JwtClaimTypes.Name,
                RoleClaimType = JwtClaimTypes.Role,
            };

            options.Prompt = "consent";
        });

You need to tweak the code to fit your need.