OAuth. Resource server uses the wrong Authentication URI (Bearer error=invalid_token)

128 Views Asked by At

In a service-to-service oauth communication, I'm trying to send a token request to an external Oauth Token Endpoint and to use the token as a Bearer for a Microsoft solution (Dynamics CRM v.9.1 On-Premises). The platform used should not be so relevant since we are talking about OAuth which is an RFC documented protocol The code I used so far to get the token and to make the request to the Resource Server with the Bearer is the one pasted below.

When I make the request to the Resource server i get the following error.

Bearer error=invalid_token, error_description=Error during token validation!, 
authorization_uri=https://mytokenserver/login, 
resource_id=https://myresourceserver/

The problem in the response is that it shows an "authorization_uri" which is https://mytokenserver/login , which is wrong. My authorization uri is : https://mytokenserver/oauth2/authorize

I wasn't able to find any setting in the microsoft crm platform so my best guess is that I'm generating a wrong Assertion JWT in the code below.

Any experience with that ? (just a small detail, the oauth token server is written in java)

    static private string clientId     = "rb7ddjkjWd8djkjlk";
    static private string pfxFile      = "C:\\keystore.p12";
    static private string pass         = "blabla";
    static private string authorityUri = "https://mytokenserver/oauth2/token";

    static private Uri environmentUri  = new Uri("https://myresourceserver/api/data/v9.1");

    static async Task<string> RequestTokenAndSendRequestToResourceServerAsync()
    {
        if (tokenCache != "")
            return tokenCache;

        var client = new HttpClient();

        var clientToken = GetJwtToken(pfxFile, pass, clientId, authorityUri, 180);

        Console.WriteLine("JWT Token is: " + clientToken);

        Parameters parameters = new Parameters
        {
            new KeyValuePair<string, string>("audience", clientId),
            new KeyValuePair<string, string>("requested_token_type", "urn:ietf:params:oauth:token-type:access_token")
        };
        ClientCredentialsTokenRequest clientCredentialsRequest = new ClientCredentialsTokenRequest
        {
            Parameters = parameters,
            GrantType = OidcConstants.GrantTypes.ClientCredentials,
            Address = authorityUri,
            ClientAssertion =
            {
                Type = OidcConstants.ClientAssertionTypes.JwtBearer,
                Value = clientToken
            }
        };
        clientCredentialsRequest.ClientCredentialStyle = ClientCredentialStyle.PostBody;
        var response = await client.RequestClientCredentialsTokenAsync(clientCredentialsRequest);

        if (response.IsError)
        { 
            Console.WriteLine(response.HttpStatusCode);
            Console.WriteLine(response.ErrorDescription);
            Console.WriteLine(response.ErrorType.ToString());
            throw response.Exception;
        }
        Console.WriteLine("Access Token is: " + response.AccessToken);

        // Set up the HTTP client
        var httpclient = new HttpClient
        {
            BaseAddress = new Uri("https://myresourceserver"),
            Timeout = new TimeSpan(0, 2, 0)  // Standard two minute timeout.
        };

        HttpRequestHeaders headers = httpclient.DefaultRequestHeaders;
        headers.Authorization = new AuthenticationHeaderValue("Bearer", response.AccessToken);
        headers.Add("OData-MaxVersion", "4.0");
        headers.Add("OData-Version", "4.0");
        headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

        // Web API call
        var result = httpclient.GetAsync("WhoAmI").Result;

        var jsonResponse = await result.Content.ReadAsStringAsync();

        Console.WriteLine(result.ReasonPhrase);
        Console.WriteLine(jsonResponse);
        result.EnsureSuccessStatusCode();

        return response.AccessToken;
    }
    

    public static string GetJwtToken(string pfxFilePath, string password, string issuer, string audience, int expiryInMinutes)
    {
        Console.WriteLine("Creating JWT Token");
        string jwtToken = string.Empty;
        JwtSecurityToken jwtSecurityToken;

        X509Certificate2 signingCert = new X509Certificate2(pfxFilePath, password);
        X509SecurityKey privateKey = new X509SecurityKey(signingCert);

        var descriptor = new SecurityTokenDescriptor
        {
            // Audience = auth2/token endpoint, Issuer = clientId                
            Issuer = issuer,
            Audience = audience,
            IssuedAt = DateTime.UtcNow,
            Expires  = DateTime.UtcNow.AddMinutes(expiryInMinutes),
            Subject  = new ClaimsIdentity(new List<Claim>
            {
                new Claim(JwtClaimTypes.Subject, issuer),
                new Claim(Microsoft.IdentityModel.JsonWebTokens.JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
            }),
            SigningCredentials = new SigningCredentials(privateKey, SecurityAlgorithms.RsaSha256)
        };

        var handler = new JwtSecurityTokenHandler();
        handler.SetDefaultTimesOnTokenCreation = false;
        jwtSecurityToken = handler.CreateJwtSecurityToken(descriptor);
        jwtSecurityToken.Header.Remove("kid");

        jwtToken = handler.WriteToken(jwtSecurityToken);
        return jwtToken;
    }
1

There are 1 best solutions below

2
On BEST ANSWER

My guess is that the resource server you're calling does not allow client credential access tokens. It probably requires a user to be a subject of the token, not your service. This could explain the error message. The server expects that the endpoint for the authorization code request is the one present in the token. You have the token endpoint (which is used in the client credentials flow).