Web API not authorized when using JwtBearerAuthenticationOptions - asp.net

I have taken section 2 from a walkthrough on how to authorize with jwt so that I can get an access token from my client and authorize them to use the api. However, I can't seem to get this to work. I keep on getting a 401 message from Postman accompanied by a:
{
"Message": "Authorization has been denied for this request."
}
Tutorial Link: http://bitoftech.net/2014/10/27/json-web-token-asp-net-web-api-2-jwt-owin-authorization-server/
The access token comes from an authorization service I have created in Java, so the first section of the tutorial does not apply to me.
JWT
{
"exp": 1489641048,
"user_name": "testuser",
"authorities": [
"USER"
],
"jti": "2dde11c3-2f06-496c-9b36-4dbf71cdc2e2",
"client_id": "webreport_service",
"scope": [
"USER"
]
}
Web API code snippet
public void ConfigureOAuth(IAppBuilder app)
{
var audience = "webreport_service";
// Api controllers with an [Authorize] attribute will be validated with JWT
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] { audience },
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider("Any", "abc123")
}
});
}
It is slightly different from what is on the link in section 2, but that is because I don't base64 encode my secret and I also do not put the issuer in my jwt.
Postman
GET /api/protected HTTP/1.1
Host: localhost:54706
Authenticate: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0ODk2NDEyOTAsInVzZXJfbmFtZSI6InRlc3R1c2VyIiwiYXV0aG9yaXRpZXMiOlsiVVNFUiJdLCJqdGkiOiJlMDNkNWZmZC1hZWI4LTRkODctOGQ3My0zNjhjYjQ2ZDg2OWUiLCJjbGllbnRfaWQiOiJ3ZWJyZXBvcnRfc2VydmljZSIsInNjb3BlIjpbIlVTRVIiXX0.C4hivwA1VF-0GO0xCVUoDIheWQWlAcVWvAzChZTgrHY
Cache-Control: no-cache
Postman-Token: ff628109-d5f4-76e0-41c2-e0c7d377b93f
Any help would be greatly appreciated.
Thanks!

I think so check the following:
Firstly:
Check your secret base64 code. 'abc123' is true?
I'm check your token in jwt.io website on your secret code.
but Invalid Signature
Secondly:
Check your payload value.
What is 'iss' your jwt payload. / your issuer validate set 'Any'
What is 'aud' your jwt payload. / your audience validate set 'webreport_service'
Think about it.
Best regard

Related

How can I add payload when generating JWT in http JWT AUTH?

This is what I could find on official ballerina learn-by-examples website.
But how can I sign payloads and return JWT on some endpoints, like we do in NodeJS using jsonwebtoken
You can use the jwt:issue() method to achieve this.
jwt:IssuerConfig issuerConfig = {
username: "user",
issuer: "wso2",
audience: "example.com",
expTime: 3600,
signatureConfig: {
config: {
keyFile: "./resources/private.key"
}
},
customClaims: {
"scope": "scope1 scope2"
}
};
string jwt = check jwt:issue(issuerConfig);
You can refer this service example for a scenario where this approach was used to implement a login endpoint.

Swagger Authorization bearer not send

I'm using Swagger Swashbuckle in a dotnet core 3.1 web api project and have trouble to send bearer authorization to the requests calls.
I've defined this in my ConfigureServices method:
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo() { Title = "MyApi", Version = "v1" });
// Set the comments path for the Swagger JSON and UI.
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
c.IncludeXmlComments(xmlPath);
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.OAuth2,
Flows = new OpenApiOAuthFlows()
{
Password = new OpenApiOAuthFlow()
{
TokenUrl = new Uri("/api/Account/Login", UriKind.Relative),
}
},
In = ParameterLocation.Header,
Name = "Authorization",
Scheme = "Bearer",
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement()
{
{
new OpenApiSecurityScheme()
{
Reference = new OpenApiReference()
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
},
Scheme = "Bearer",
Type = SecuritySchemeType.Http,
Name = "Bearer",
In = ParameterLocation.Header
}, new List<string>()
}
});
});
When running, I see the Authorize button that show the login dialog:
Once logged, the API routes show the locked padlock but when I try to use them, I see the call is done without the returned bearer:
curl -X GET "http://localhost:5000/api/Account" -H "accept: */*" -H "Authorization: Bearer undefined"
What's wrong with my definitions?
In the cURL request you can see: -H "Authorization: Bearer undefined".
This means that when Swagger-UI tries to get the token that will be added to the request header, it cannot be found.
Then, where the token cames from, and why Swagger-UI cannot found it? The token comes in the json returned from your login endpoint (/api/Account/Login).
You must be sure that returned json from your login endpoint repect the expected format for a OAuth password flow, as explained in the RFC6749 section 4.1.4 (Access Token Response).
From your login endpoint you must return a json response like this:
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"bearer"
}
It's a common mistake, that when you serialize the response from the controller you dont respect the json property names. In example: you can be returning a json like this:
{
"accessToken":"2YotnFZFEjr1zCsicMWpAA",
"tokenType":"bearer"
}
Where "accessToken" is not the same as "access_token" and so on.
This little difference causes that Swagger-UI cannot found the bearer token when it deserializes the returned json.
TIP: decorate the "AccessToken" property of your response object, so it will be serialized correctly.
[JsonPropertyName("access_token")]
[JsonProperty(PropertyName = "access_token")]
public string AccessToken { get; set; }
Although what is explained is the main point of your problem, I want to tell you that adding the security requirement globally is not the most correct way to do it. In this way you are protecting all the endpoints, regardless of whether they are decorated with the Authorize attribute or not.
In the startup you only must set a "Security Definition", and add an "OperationFilter" that handles the "security requirements". Then "SecurityRequirement" references the "SecurityDefinition", then you don't repeat the security definition configuration (Scheme, Type, Name, In, etc.) inside the security requirements as you are doing in your example.
Refer to this github post that shows you the correct way to do it.

JWT Authorization policy to Ignore expiration (Lifetime)

I am working with JWT authentication in Asp.net core 3.0. The application is already setup to use JWT authentication and it is working fine. What I am looking for is to have an endpoint to be able to accessed even the auth token is expired (but all the other checks must be validated). I read about the policies and authentication schemes and played with those but it didn't help. How this can be made possible? Any help would be appreciated. TIA
That defeats the whole purpose of the system; why would you want that?
You could technically just increase the lifetime of the token, an example with IdentityServer4:
int tokenLifetime = (int)TimeSpan.FromDays(30).TotalSeconds;
services.AddIdentityServer()
.AddInMemoryClients(new[] {
new Client
{
ClientId = "client",
AllowedGrantTypes = GrantTypes.ClientCredentials,
ClientSecrets =
{
new Secret("some_secret".Sha256())
},
AllowedScopes = { "your-scope" },
AccessTokenLifetime = tokenLifetime
}
});
Another way is to use refresh tokens to renew your access token.

Getting Request had invalid authentication credentials error FCM

Am trying to test my web push notification from postman
my app id is thepostman-2018 so I am sending post requests to the url
https://fcm.googleapis.com/v1/projects/thepostman-2018/messages:send
event though I have set Authentication header and passed my Server key
i am getting this response
{
"error": {
"code": 401,
"message": "Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",
"status": "UNAUTHENTICATED"
}
}
FCM v1 requests do not use the API key from the Firebase console to authorize requests. Instead, they use credentials retrieved by authenticating using the Service Account Key downloaded from the Firebase console. For example, this is how you generate the token using Node.js:
function getAccessToken() {
return new Promise(function(resolve, reject) {
var key = require('./service-account.json');
var jwtClient = new google.auth.JWT(
key.client_email,
null,
key.private_key,
SCOPES,
null
);
jwtClient.authorize(function(err, tokens) {
if (err) {
reject(err);
return;
}
resolve(tokens.access_token);
});
});
}
See the guide for more details.

How to request a refresh token with identityserver4

I have IdentityServer and a separate WebApi project using resource owner flow.
When I request a token as below the token is issued and can be used to access the WebApi. So I assume IdentityServer is setup correctly, and the WebApi project is setup correctly as well.
username=user1
password=user1password
grant_type=password
client_id=myClientId
client_secret=myClientSecret
scope=api1
Now when I change the grant type to refresh and the scope to offline_access I get a refresh token. I then use the refresh token to get an access token, but when I use the access token to request the WebApi it is rejected.
With an error
the audience is invalid
I suspect it's because I am asking for an offline_access scope instead of api1 scope which the WebApi project expects. How do I get a refresh token that can use used with the scope api1?
var model = {
client_id: "myClientId",
client_secret: "myClientSecret",
scope: "api1 offline_access",
token_type: "Bearer", //Optional
grant_type: "refresh_token",
refresh_token: "your refresh token"
};
//this is most important step when to use refresh token
var base64 = btoa(model.client_id + ":" + model.client_secret);
//and your request here
this.$http({
method: "POST",
url: "/connect/token",
headers: {
'content-type': "application/x-www-form-urlencoded",
'Authorization': "Basic " + base64
},
data: jQuery.param(model)
})
.then(
response => {
//success
},
response => {
//logout
});

Resources