JWT validation with external authority doesn't work. ASP .NET Core 3.0 - asp.net-core

I am trying to authorize my requests using external service with IdentityServer4. I use following code
services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
options.Authority = Configuration["IdentityUrl"];
options.RequireHttpsMetadata = false;
options.Audience = "myapi";
});
I got my token using token client
var tokenResponse = await tokenClient.RequestPasswordTokenAsync(
new PasswordTokenRequest
{
ClientId = clientId,
ClientSecret = clientSecret,
GrantType = GrantTypes.Password,
Address = discoveryDocument.TokenEndpoint,
UserName = user.UserName,
Password = user.PasswordHash,
});
It works and gives me token, but then when I try to authorize any request by including this token into authorization header it gives me 401 with no explanation. I don't see anything in output of either idenity server application or my client application.
This is what I do in postman to test authorization
Any ideas on what is wrong and how to debug it?

Try to see what the logs says?
Or try to set this one to true in the AddJwtBearer options and then look at the response from the API to see if it contains some additional details about the failure.
options.IncludeErrorDetails = true;
It it works you should see an additional WWW-authenticate header, like this one:
HTTP/1.1 401 Unauthorized
Date: Sun, 02 Aug 2020 11:19:06 GMT
WWW-Authenticate: Bearer error="invalid_token", error_description="The signature is invalid"

Related

xero api fails with unauthorized (401 or 403) after calling auth, refresh and gettenants when calling getinvoices

I'm a rank noob at this, so excuse my ignorance. I've got an MVC web application to login, get the access and refresh tokens, and tenant list OK. I can even get it to refresh the refresh token. No problems.
When I try to run the GetInvoices endpoint either directly or via the sdk, I get 403 (skd) or 401 from the direct api call.
From the latest run with direct call I get this response
{StatusCode: 401, ReasonPhrase: 'Unauthorized', Version: 1.1, Content:
System.Net.Http.HttpConnectionResponseContent, Headers:
{
Server: nginx
Strict-Transport-Security: max-age=31536000
WWW-Authenticate: OAuth Realm="api.xero.com"
Cache-Control: no-store, no-cache, max-age=0
Pragma: no-cache
Date: Wed, 21 Jul 2021 11:19:56 GMT
Connection: close
X-Client-TLS-ver: tls1.2
Content-Type: text/html; charset=utf-8
Content-Length: 95
Expires: Wed, 21 Jul 2021 11:19:56 GMT
}, Trailing Headers:
{
}}
I know that the access token and tenant id used in the GetInvoices step are correct because I checked them against the values pulled in from the auth steps character by character.
The app is being run in Visual Studio 2019, using the self-signed development SSL certificate.
Why is it rejecting the request?
my controllers have the following
private static readonly string Scopes = "openid offline_access profile email accounting.transactions accounting.contacts accounting.attachments";
private static readonly string Scopes = "openid offline_access profile email accounting.transactions accounting.contacts accounting.attachments";
string[] tenant = (string[])TempData.Peek("tenant");
var client = new HttpClient();
var formContent = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("summaryOnly", "true"),
});
client.DefaultRequestHeaders.Add("Authorization", (string)TempData.Peek("accessToken"));
client.DefaultRequestHeaders.Add("Xero-Tenant-Id", tenant[0]);
client.DefaultRequestHeaders.Add("Accept", "application/json");
var response = await client.PostAsync("https://api.xero.com/api.xro/2.0/Invoices", formContent);
The SDK should handle this for you in the helper methods for the client and OAuth flow but i've included what looks like is missing from just a raw API call below.
Core API call - looks like you need to prefix the token with the Bearer string.
Authorization: "Bearer " + access_token
If you are wanting to use the SDK note that there is a sub Nuget package for OAuth helpers that will help you obtain an access token which you need to pass to core api calls.
https://github.com/XeroAPI/Xero-NetStandard/tree/master/Xero.NetStandard.OAuth2Client
(DOH!) The Tenant returns an Id and a TenantId. I was using the Id.
Thanks to SerKnight and droopsnoot for helping.
I've added code from the OAuth2. The help does not mention to get and cast the return type of RequestAcessTokenAsync.
XeroOAuth2Token authToken = (XeroOAuth2Token)await client.RequestAccessTokenAsync(authorisationCode);
also to check the state on return, you must set in xconfig. mine reads
XeroConfiguration xconfig = new()
{
ClientId = Global.XeroClientID,
ClientSecret = Global.XeroClientSecret,
CallbackUri = new Uri(Global.RedirectURI),
Scope = Global.XeroScopes,
State = Global.XeroState
};
var client = new XeroClient(xconfig);
return Redirect(client.BuildLoginUri());

MSGraph Authenticating to dynamics with correct token gives 401

Using the following code I can connect to MSGraph (different resource url) and fetch the data that I need, but I'm struggling to realise how to do the same for Microsoft Dynamics (CRM)
public static async Task<string> GetTokenForApplication(string resourceUrl)
{
AuthenticationContext authenticationContext =
new AuthenticationContext(AppModeConstants.AuthString, false);
ClientCredential clientCred = new ClientCredential(AppModeConstants.ClientId, AppModeConstants.ClientSecret);
AuthenticationResult authenticationResult = await authenticationContext
AcquireTokenAsync("resourceUrl", clientCred);
TokenForApplication = authenticationResult.AccessToken;
}
public static async Task CrmTest()
{
var token = await AuthenticationHelper.GetTokenForApplication(#"https://myapp.crm4.dynamics.com/");
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await client.GetAsync(#"https://myapp.crm4.dynamics.com/api/data/v8.2/accounts");
}
Although I fetch the right token I'll still get a 401: Unauthorized:
{StatusCode: 401, ReasonPhrase: 'Unauthorized', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
REQ_ID: 790b38b0-c8c0-4878-a318-e490ee7db57a
Strict-Transport-Security: max-age=31536000; includeSubDomains
Date: Thu, 11 May 2017 15:33:41 GMT
Set-Cookie: crmf5cookie=!UXdAbawdawdal8sNiJ9xy74dAiawdawdDnDsomethingAolDYjkR7innjWIYQ1pV+DcZ9A=;secure; path=/
Server: Microsoft-IIS/8.5
WWW-Authenticate: Bearer authorization_uri=https://login.windows.net/b8faag21-awda-awda-90s4-f8652ss86ddb/oauth2/authorize, resource_id=https://myapp.crm4.dynamics.com/
X-Powered-By: ASP.NET
Content-Length: 49
Content-Type: text/html
}}
A 403 I would understand, but a 401 means that I'm simply using the wrong authentication. Any input on this would be appreciated.
Edit: Is it even possible to access CRM data as an application? There are only delegated permissions available in the azure portal, no application permissions.
While both Dynamics CRM and Graph leverage Azure AD accounts, they have different permission scopes and prerequisites.
To get started, you'll want to follow this Walkthrough: Register a Dynamics 365 app with Azure Active Directory.

Identity Server 4 Scopes found on current principal: ""

I attempting to use Identity Server 4 to authenticate users before granting access to a API. I am using a Implicit configuration as the front end is a Ember JS app. I have been able to Login, display the consent screen and then navigate to the redirectUri. However as soon as the Bearer Token is sent to the API I get back a 403.
I was able to find in the logs right before the 403 is returned it states there are not any scopes specified for the current principle. However I have yet to find anyone else reporting the same issue so I am not sure what I am doing wrong.
Any help would be appreciated.
API LOG
2017-03-27 15:24:35.358 -05:00 [Information] Request starting HTTP/1.1 GET http://localhost:55026/incentives/api/categories application/json
2017-03-27 15:24:36.035 -05:00 [Information] Successfully validated the token.
2017-03-27 15:24:36.046 -05:00 [Information] HttpContext.User merged via AutomaticAuthentication from authenticationScheme: "Bearer".
2017-03-27 15:24:36.048 -05:00 [Information] AuthenticationScheme: "Bearer" was successfully authenticated.
2017-03-27 15:24:36.051 -05:00 [Information] Scopes found on current principal: ""
2017-03-27 15:24:36.053 -05:00 [Warning] Scope validation failed. Return 403
2017-03-27 15:24:36.059 -05:00 [Debug] Connection id ""0HL3L9SNQEILQ"" completed keep alive response.
2017-03-27 15:24:36.060 -05:00 [Information] Request finished in 702.0523ms 403
new Client
{
ClientName = "IncentivesClient",
ClientId = "IncentivesClient",
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true,
RedirectUris = new List<string>
{
"http://localhost:4200/authorized"
},
PostLogoutRedirectUris = new List<string>
{
"http://localhost:4200/unauthorized"
},
AllowedCorsOrigins = new List<string>
{
"http://localhost:4200"
},
AllowedScopes = new List<string>
{
StandardScopes.OpenId,
StandardScopes.Email,
StandardScopes.Profile,
"incentiveRecords",
"incentiverecordsscope",
}
}
This is the API settings
IdentityServerAuthenticationOptions identityServerValidationOptions = new IdentityServerAuthenticationOptions
{
Authority = "https://localhost:44357",
RequireHttpsMetadata = true,
AllowedScopes = new List<string> { "incentiveRecords" },
ApiSecret = "incentiveRecordsSecret",
ApiName = "incentiveRecords",
AutomaticAuthenticate = true,
SupportedTokens = SupportedTokens.Both,
AuthenticationScheme = "Bearer",
SaveToken = true,
ValidateScope = true,
// TokenRetriever = _tokenRetriever,
// required if you want to return a 403 and not a 401 for forbidden responses
AutomaticChallenge = true,
};
The example that I had found for implementing your own torii provider for OATH2 had instructed to include 'id_token' and 'token' in the response params which made sense considering they are the response type being utilized in my Implicit configuration. To add to the confusion both the id_token and token were obtaining values successfully after the promise. It did bother me that they were identical and when I decoded them the audience was set to the client instead of including the resource server. So the super simple answer was to change the 'token' response param to 'access_token'. Now I am able to obtain the correct token from the promise and move on to utilizing it within the Auth header. Hope this helps someone else out there.

How to get id_token from TokenEndpoint of IdentityServer4 through authorization_code flow?

I would like to get "access_token" and "id_token" from Token endpoint through Authorization Code flow. But, I am getting "invalid_grant" error while calling the token endpoint with following parameters on postman.
POST /connect/token HTTP/1.1
Host: localhost:2000
Content-Type: application/x-www-form-urlencoded
Cache-Control: no-cache
Postman-Token: a8a29659-0ea3-e7dc-3bd6-6e6630a7370d
client_id=client
&client_secret=client
&grant_type=authorization_code
&username=admin
&password=admin
&scope=openid+profile
Client Configuration:
new Client
{
ClientId = "client",
ClientSecrets =
{
new Secret("client".Sha256())
},
AllowedGrantTypes = new List<string> { OidcConstants.GrantTypes.AuthorizationCode },
AllowedScopes = {
StandardScopes.OpenId.Name,
StandardScopes.Profile.Name,
}
}
What is wrong in my client configuration section? and, How do i make a successful post request to Token Endpoint?
The authorization code grant type requires a code parameter to be sent during the token request (see RFC6749 section 4.1.3).
This code is issued by the authorization server after the resource owner authorized the client (see RFC6749 section 4.1.2).

coinbase oauth2 sometimes failed to renew access token (using refresh token)

I am integrating coinbase in an iOS app and I am using Oauth2 to authenticate. I am able to get the access token after going through the usual workflow. I have also taken care to attempt refresh my access token whenever any requests hit a 401 (upon expiration) by calling this:
POST https://coinbase.com/oauth/token
Data:
grant_type=refresh_token&refresh_token=abcd1234&client_id=theclientid&client_secret=somesecretid
It works for a while but then from time to time, it would fail with a request response:
NSHTTPURLResponse: 0x15eb2730
{ URL: https://coinbase.com/oauth/token } { status code: 401, headers {
"CF-RAY" = "f67d477aae4052e-YYZ";
"Cache-Control" = "no-store";
Connection = "keep-alive";
"Content-Type" = "application/json; charset=utf-8";
Date = "Sun, 02 Feb 2014 15:14:14 GMT";
Pragma = "no-cache";
Server = "cloudflare-nginx";
"Set-Cookie" = "__cfduid=<some long alpha-numeric string>; expires=Mon, 23-Dec-2019 23:50:00 GMT; path=/; domain=.coinbase.com; HttpOnly";
Status = "401 Unauthorized";
"Strict-Transport-Security" = "max-age=31536000";
"Transfer-Encoding" = Identity;
Vary = "Accept-Encoding";
"Www-Authenticate" = "Bearer realm=\"Doorkeeper\", error=\"invalid_request\", error_description=\"The request is missing a required parameter, includes an unsupported parameter value, or is otherwise malformed.\"";
"X-Content-Type-Options" = nosniff;
"X-Frame-Options" = SAMEORIGIN;
"X-Rack-Cache" = "invalidate, pass";
"X-Request-Id" = "<some long alpha-numeric string>";
"X-Runtime" = "0.012066";
"X-Ua-Compatible" = "IE=Edge,chrome=1";
} }
Has anyone encounter this error before? I have assume the request URL is correct always. I am not sure why it would complained about "missing required parameter" or "unsupported parameter". I havent figured out a pattern of failure yet. Hopefully, someone out there may have seen this before.
I haven't been able to find any documentation directly about refresh_token requests, but I think you're supposed to also include the redirect_uri in your refresh_token request (based on this: https://coinbase.com/docs/api/authentication#collapse2).
Also, I noticed that my official coinbase app required a re-auth a few days ago, but when I log in to my coinbase account, it says that the app was authorized 25 days ago. So, perhaps even the request_tokens have a timeout? Did your request_token request fail after not using the app for a while?
Or maybe coinbase reset something and invalidated all of their access_tokens and refresh_tokens because my app, which was previously working fine as far as refresh_tokens, is now failing on the refresh_token request.
So, I would suggest having your app re-authorize when this happens, and get a new access_token and refresh_token, since I think that's what the official coinbase app does.