Error accessing Stream REST Api using JWT - getstream-io

I am trying to use JWT token to access a Stream feed but it is returning 404 everytime.
Token header:
{
"alg": "HS256"
}
Token payload:
{
"resource": "feed",
"action": "read"
}
The token was generated using the jjwt library and signed using the secret provided in my account dashboard. I also validated it on jwt.io and it was a valid token
Url
GET: https://api.getstream.io/api/v1.0/feed/notification/666?api-key=...
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJyZXNvdXJjZSI6ImZlZWQiLCJhY3Rpb24iOiJyZWFkIn0.MUZHXQg0UD6jFpCZN5Mn1e7wwys_1qYuVtfBKtHL8QU
Response
{
"exception": "GetStreamAPI404",
"detail": "sorry you've hit a 404"
}
Am I missing something here? Can't figure out what the problem is.

In order to perform correct permission checking, you need to include the feed_id field in your token payload. The value of the field must be the same as the feed that you are trying to read and be in the form of ${feed_group}:${feed_id}.
For instance, the payload for the request in your question (https://api.getstream.io/api/v1.0/feed/notification/666?api-key=) should be:
{
"resource": "feed",
"action": "read",
"feed_id": "notification:666"
}
Note: API URLs must end with a trailing slash (eg. /api/v1.0/feed/notification/666/?api_key=...)
Auth HTTP headers:
Authorization must only contain the JWT token
stream-auth-type must be sent with the value jwt

Related

How to get access_token using token endpoint in the node OIDC provider

I tried to get access_token and refresh_token using authorization code flow using node oidc provider. I got auth_code. but I could not get access token and refresh token How to fix this Issue. I referred many documentation but I could not get it.
OIDC Configuration
const oidc = new Provider('http://localhost:3000', {
clients: [
{
client_id: 'foo',
client_secret: 'bar',
redirect_uris: ['https://jwt.io'], // using jwt.io as redirect_uri to show the ID Token contents
response_types: ['code'],
grant_types: ['authorization_code'],
token_endpoint_auth_method: 'none',
},
],
cookies: {
keys: 'secretkey'
},
pkce: {
required: true
},
});
// Heroku has a proxy in front that terminates ssl, you should trust the proxy.
oidc.proxy = true;
app.use(oidc.callback())
I got auth_code also
How to get access token and refresh token using node-oidc provider
Your access token request is missing the PKCE code_verifier parameter.
your client's authentication method is set to none, so you're not supposed to pass any authorization header.
you can start your provider process with DEBUG=oidc-provider:* to get more details for these errors.
Invalid Client but you have input "client_id", it mean you are enabling features:
{
clientCredentials: {
enabled: true
}
}
So you must provide client_secret
and in oidc-provider source I see it always check code_verifier so you should provide it

Microsoft Graph API, DELETE request response Error code 403 ""Access is denied. Check credentials and try again."

I am working on a Microsoft Graph API app where i want to delete email messages from the inbox.
I do that by first getting the emails, putting the id from each email in an array and for each id in that array making a delete request to remove it from the inbox.
When I try and run my code i get the error
Error: Server responded to *request link* with status code 403:
{
"error": {
"code": "ErrorAccessDenied",
"message": "Access is denied. Check credentials and try again.",
"innerError": {
"request-id": "*request id*",
"date": "2020-06-03T09:12:06"
}
}
}
When making the delete request I pass in the same Access Token that i use to make the get request for getting the email data.
Here is my code:
// This is only the code for the delete request, passing the Access Token works aswell as passing the IDList array. It loops trough all the id's and tries to make a request. But the request fails
removeEmail(IDList, AccessToken);
function removeEmail(idList, accessToken) {
idList.forEach(ID => {
var deleteEmails = request('DELETE', `https://graph.microsoft.com/v1.0/me/messages/${ID}`, {
headers: {
'Authorization': `Bearer ${accessToken}`
}
});
deletedEmails = JSON.parse(deleteEmails.getBody('utf8'));
console.log(deleteEmails);
});
}
How can this be fixed so that it removes the messages from the inbox without any problems?
Thanks in advance!
What permissions have you added to your Microsoft Graph API app?
Deleting an email requires Mail.ReadWrite - https://learn.microsoft.com/en-us/graph/api/message-delete?view=graph-rest-1.0&tabs=http
On the other hand, getting a list of messages works with the following permissions - Mail.ReadBasic, Mail.Read, Mail.ReadWrite - https://learn.microsoft.com/en-us/graph/api/user-list-messages?view=graph-rest-1.0&tabs=http#permissions
To be able to both read and delete email messages, you need Mail.ReadWrite added to your app.

How to get REFRESH_TOKEN_AUTH request to return RefreshToken

I am using Amazon Cognito to login users and save a RefreshToken so they don't have to type their password after the initial setup. I need to be able to login with the RefreshToken and get a new RefreshToken to save for next time. However, when I call InitiateAuthAsync, it does not return the RefreshToken.
C#:
var refreshReq = new InitiateAuthRequest();
refreshReq.ClientId = _clientId;
refreshReq.AuthFlow = AuthFlowType.REFRESH_TOKEN_AUTH;
refreshReq.AuthParameters.Add("SECRET_HASH",
SecretHash(_clientId, _clientSecret, username));
refreshReq.AuthParameters.Add("REFRESH_TOKEN", refreshToken);
var clientResp = cognitoProvider.InitiateAuthAsync(refreshReq).Result;
Response:
{
"AuthenticationResult": {
"AccessToken": "<accessToken>",
"ExpiresIn": 3600,
"IdToken": "<idToken>",
"TokenType": "Bearer"
},
"ChallengeParameters": {}
}
And this is the response from the login with a working ResponseToken:
{
"AuthenticationResult": {
"AccessToken": "<accessToken>",
"ExpiresIn": 3600,
"IdToken": "<idToken>",
"RefreshToken": "<refreshToken>",
"TokenType": "Bearer"
},
"ChallengeParameters": {}
}
Apparently this is a bug in the AWS Cognito API. The docs say that InitiateAuth should return an updated RefreshToken, but it does not.
The refresh token is only returned in the initial sign in and it can be used more than once to get a new access token and id token. Be aware of your user pool configuration because it will not work if you are using device tracking and the device use to sign in is not confirmed.
The refresh token is a long-lived token and there's no point returning it as it's still valid for many days. If the default 30-day expiry time is not long enough you can increase it to up to 3650 days.
I agree with the main question here there is an issues if one can't refresh the refresh token.
The best security practice is to regenerate a new Access Token and a new Refresh Token every X minutes. This way if a malicious 3rd party player get a hold on the Access Token / Refresh Token - they will be valid until the next cycle of refreshing the token by the application.
This is very weird that AWS are not supporting such mechanism that is commonly used in the web - such as Passport ID and authentication etc.

How to validate Microsoft Graph API jwt access_token and secure your API?

Scenario:
I have an angular5 client application, which uses hello.js to authenticate users using their office 365 credentials.
Client Code:
hello.init({
msft: {
id: configuration.AppID,
oauth: {
version: 2,
auth: 'https://login.microsoftonline.com/' + configuration.TenantID + '/oauth2/v2.0/authorize'
},
scope_delim: ' ',
form: false
},
},
{ redirect_uri: configuration.redirecturl }
);
}
login() {
hello('msft').login({ scope: 'User.Read People.Read', display: 'popup' })
.then((authData: any) => { // console.log(authData);
this.zone.run(() => {
// get profile
}
A successful response is (Manipulated for security reasons)
{
"msft":{
"access_token":"REMOVED TOKEN HERE",
"token_type":"Bearer",
"expires_in":3599,
"scope":"basic,User.Read,People.Read",
"state":"",
"session_state":"3b82898a-2b3f-445363f-89ae-d9696gg64ad3",
"client_id":"672330148-2bb43-3080-9eee-1f46311f789c",
"network":"msft",
"display":"popup",
"redirect_uri":"http://localhost:5653/",
"expires":15245366.218
}
}
The decoded access_token has these few keys:
Header:
1. nonce (requires some special processing, I couldn't find any documentation regarding special processing)
2. typ: JWT
PayLoad:
"aud": "https://graph.microsoft.com",
Once the access_token is received, I am sending the access_token in authorization header of every call to my backend API. The goal is to validate the token and only send a successful response if the access_token is validated and authorized. If unsuccessful, 401 Unauthorized is the response.
API Code to validate access_token, ASP .NET CORE 2, Following (https://auth0.com/blog/securing-asp-dot-net-core-2-applications-with-jwts/)
namespace JWT
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["Jwt:Issuer"],
ValidAudience = Configuration["Jwt:Issuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
};
});
services.AddMvc();
}
}
}
// other methods
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseAuthentication();
app.UseMvc();
}
In appsettings.json I have:
{ "Jwt": {
"Key": "verySecretKey", **(I got the key from https://login.microsoftonline.com/common/discovery/keys with the kid value in access_token header)**
"Issuer": "https://sts.windows.net/49bcf059-afa8-4bf9-8470-fad0c9cce27d/", } }
Finally, the error I receive is :
"WWW-Authenticate →Bearer error="invalid_token", error_description="The signature key was not found""
I have been stuck here since past few days, any help will be life savior.
Key Points:
I tried to validate the access_token in jwt.io (https://nicksnettravels.builttoroam.com/post/2017/01/24/Verifying-Azure-Active-Directory-JWT-Tokens.aspx) but I was not able to.
The aud here is https://graph.microsoft.com, I am not sure if I need to and why do I need to change aud to my client id. how do I do that?
Is there something wrong in the code or do i need to tweak the way I am requesting header tokens.
Please let me know if you need more information.
I tried to validate the access_token in jwt.io (https://nicksnettravels.builttoroam.com/post/2017/01/24/Verifying-Azure-Active-Directory-JWT-Tokens.aspx) but I was not able to.
Microsoft Graph API access tokens are signed differently from other access tokens from what I can see.
You do not need to validate tokens that are meant for another API, it is their job.
The aud here is https://graph.microsoft.com, I am not sure if I need to and why do I need to change aud to my client id. how do I do that?
I don't know about HelloJS, but you should be able to get an Id token after authentication with response_type=id_token token.
Then you need to attach that to the requests.
It should have your client id as the audience.
Is there something wrong in the code or do i need to tweak the way I am requesting header tokens.
The only thing that stands out to me is that you are doing a lot of unnecessary configuration.
Basically the configuration should be:
.AddJwtBearer(o =>
{
o.Audience = "your-client-id";
o.Authority = "https://login.microsoftonline.com/your-tenant-id/v2.0";
})
The handler will automatically fetch the public signing keys on startup.
It's not really a good idea to hard-code signing keys in your app since your app will break when AAD finishes signing key rollover.
I also spent a lot of time trying to validate it, but the bottom line is that you can't:
Access tokens are opaque blobs of text that are for the resource only. If you're a client getting a token for Graph, assume that it's an encrypted string that you should never look at - sometimes it will be. We use a special token format for Graph that they know how to validate - you shouldn't be looking at access tokens if they're not for you. (source: https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/609)
Instead of using the access token, you should create an ID token, which is a regular JWT token that can be validated like any other JWT:
Get the public key from the Microsoft directory
Validate the signature, audience, issuer, etc.
To get an ID token using the MSAL API after login you can do (javascript example):
const { instance, accounts } = useMsal();
const request = {
scopes: ["User.Read"],
account: accounts[0]
};
const idToken = await instance.acquireTokenSilent(request).idToken;
For more information on ID Tokens, please check:
https://learn.microsoft.com/en-us/azure/active-directory/develop/id-tokens
For more information on opaque tokens, please check:
https://zitadel.com/blog/jwt-vs-opaque-tokens
Yeah, this took a bit to work through. For anyone else researching this, here's my understanding.
You don't use the Microsoft Graph API to secure your web api. Instead:
The client continues to use the Microsoft Identity Platform to authenticate.
The client uses the resulting JWT access token to call the Web API as normal for OAuth 2.0 flow
The web API uses JwtBearerAuthenticationScheme, setting the authority to the Microsoft identity platform. See this example and search for JwtBearerAuthenticationScheme.
The web API uses the provided access token to obtain an 'On Behalf Of' user token.
The web API calls the Graph API using this 'On Behalf Of' token. This token has a different lifespan than the token the client obtained, and refreshes must be handled separately.
This is a very distilled version of this example. Disclaimer: I haven't put this into practice yet.

How to get access_token, id_token from authorize endpoint of IdentityServer4?

This question is actually a continuous question of this SO question of mine. I am trying to get access_token and id_token from Identityserver4 by using Authorization code flow.
But, If I try to access "Authorize" endpoint, I got 405 (method not allowed) HTTP error.
HTTP GET Request
http://localhost:2000/connect/authorize?
client_id=client
&client_secret=secret
&grant_type=authorization_code
&username=admin
&password=admin
&response_type=id_token+token
&scope=openid+profile+offline_access
Client:
new Client
{
ClientId = "client",
ClientSecrets = { new Secret("secret".Sha256())},
AllowedGrantTypes = new List<string> { "authorization_code" },
AccessTokenType = AccessTokenType.Jwt,
AllowedScopes = { StandardScopes.OpenId.Name, "api1" }
}
User:
new InMemoryUser
{
Subject = "1",
Username = "admin",
Password = "admin"
}
My question is, How to call authorize endpoint to get access_token and id_token? What's wrong in my "client" and "user" configuration?
Two ideas:
The HTTP 405 error can be due to the web browser's same origin policy. Your client looks like a confidential client not a browser-based client, though, and that means the same origin policy does not apply, unless you are mistakenly making that request through a web browser.
That HTTP 405 error can also happen when you use an HTTP verb that is not allowed. For instance, if you use a POST when the URL allows only a GET. Make 100% sure that you are making a GET request.
You have several issues. You are mixing up multiple flows.
1) If you want to get an id_token back from the Authorize endpoint (and not the Token endpoint) you need to use Hybrid flow... not authorization code flow. See here. So you'd need to change your response type accordingly. If your client was a SPA you could use implicit flow and get an id_token and access_token from the Authorize endpoint - but not authorization code flow.
2) client_secret is not a parameter for the Authorize endpoint. Neither is grant_type. See here for the valid parameters.
3) you don't send username and password to the Authorize endpoint under any circumstances. If you are using resource owner flow you'd send them to the Token endpoint - but never Authorize. See the above link with the description of valid parameters.
So you can switch to hybrid flow and change your code to this:
http://localhost:2000/connect/authorize?
client_id=client
&redirect_uri=<add redirect uri>
&response_type=code+id_token+token
&scope=openid+profile+api1
&state=...
The response from this call will include id_token and access_token.
new Client
{
ClientId = "client",
ClientName = "Your Client",
AllowedGrantTypes = GrantTypes.Hybrid,
ClientSecrets =
{
new Secret("secret".Sha256())
},
RedirectUris = { "<add redirect uri>" },
PostLogoutRedirectUris = { "<add post logout redirect uri>" },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"api1"
}
};

Resources