Created a user pool client using Cognito Identity Provider Client SDK for JavaScript v3, but can't fetch token using (client_credentials) grant type - oauth-2.0

Created a user pool client using Cognito Identity Provider Client SDK for JavaScript v3
npm install #aws-sdk/client-cognito-identity-provider.
The following code shows how I created the resources server and the user pool client, using the mentioned👆 SDK...
let poolName = 'UserPool';
const client =new CognitoIdentityProviderClient({
region: process.env.COGNITO_AWS_REGION
});
// create resource server
const createResourceServerCommand = new CreateResourceServerCommand({
Name: poolName,
UserPoolId: UserPool.Id,
Identifier: 'https://localhost:8080/api/v2',
Scopes: [
{
ScopeName: 'access',
ScopeDescription: 'General access to API'
}
]
});
const { ResourceServer } = await client.send(createResourceServerCommand);
// create the user pool client
const createUserPoolClientCommand = new CreateUserPoolClientCommand({
ClientName: 'Default',
UserPoolId: UserPool.Id,
ExplicitAuthFlows: ['USER_PASSWORD_AUTH'],
GenerateSecret: true,
AllowedOAuthFlows: ['client_credentials'],
SupportedIdentityProviders: ['COGNITO'],
AllowedOAuthScopes: [ 'https://localhost:8080/api/v2/access' ]
});
const { UserPoolClient } = await client.send(createUserPoolClientCommand);
...but, I can't fetch tokens using the grant type client_credentials. Therefore getting the following error.
{
"error": "invalid_grant"
}
However, if I use AWS console to navigate to the user pool > Client > Edit the hosted UI and click on the save button without making any changes...
... I am able to fetch a token using the client_credentials grant type.
Is there any setting that I might be missing in the above code that AWS console is setting? I need the following code to automate the creation of user pools.

When I switched to the old I noticed this notification
Apparently, Oauth flows are not enabled by default. Hence adding the following attribute to the CreateUserPoolClientCommandInput object AllowedOAuthFlowsUserPoolClient: true enables it. Hope this helps some newbie like me out there.

Related

Issue authenticating to Auth0 via Swagger UI (.NET) with client credentials

I'm trying to authenticate to my Auth0 tenant via the Swagger UI (auto-generated by Swashbuckle.AspNetCore) using client credentials.
I'm getting the following error:
Auth ErrorError, error: access_denied, description: Non-global clients
are not allowed access to APIv1
Here's a screen shot:
Open API spec looks like this:
"securitySchemes": {
"auth0": {
"type": "oauth2",
"flows": {
"clientCredentials": {
"tokenUrl": "https://example.auth0.com/oauth/token"
},
"authorizationCode": {
"authorizationUrl": "https://example.auth0.com/authorize?audience=test",
"tokenUrl": "https://example.auth0.com/oauth/token",
"scopes": { }
}
}
}
}
I suspect this has something to do with the audience not being specified. I was having a similar issue with authorization code flow, but manged to get this working by appending the audience as a query-string param to the authorizationUrl (as shown above). Unfortunately the same trick doesn't work with client credential flow (i.e. attempting to append the audience to the tokenUrl). I need to support client credential flow, as one or more of the routes need to be locked down to M2M tokens only.
Interestingly, it works if I use the global client ID / secret (found under the advanced settings of the tenant), but I'm not sure if we should use this...
Anyone else ran into this issue and if so, any luck with finding a solution?

#feathersjs/authentication-oauth2 not creating JWT and user

I cannot authenticate to a FeathersJS server using OAuth2 Facebook strategy because after Facebook grants access to user profile, the feathers-authentication-oauth2 plugin doesn't create the user into the DB and it also doesn't create the required JWT token to be authenticated when calling feathersclient.authenticate() in the client app.
I've tried to follow all documents I've found that explain how to do it, but as a good example I could select this one (https://blog.feathersjs.com/how-to-setup-oauth-flow-with-featherjs-522bdecb10a8) that is very well explained.
As a starting point I've taken the Feathers chat application explained at the documentation (https://docs.feathersjs.com/guides/chat/readme.html) after having it working properly, I've added tha OAuth2 part as explained in the Medium document. In the default.json file I've added the "facebook" authentication strategy:
"facebook": {
"clientID": "MY_CLIENT_ID",
"clientSecret": "MY_CLIENT_SECRET"
}
In the authentication.js fileI've added the configuration of the Facebook OAuth2 authentication:
const authentication = require('#feathersjs/authentication');
const jwt = require('#feathersjs/authentication-jwt');
const oauth2 = require('#feathersjs/authentication-oauth2');
const FacebookStrategy = require('passport-facebook').Strategy;
module.exports = function (app) {
const config = app.get('authentication');
// Set up authentication with the secret
app.configure(authentication(config));
app.configure(jwt());
app.configure(oauth2({
name: 'facebook',
Strategy: FacebookStrategy,
callbackURL: '/',
scope: ['public_profile', 'email'],
}));
...
And finally, in src/app.js file I've added a new "Facebook login" button that just changes window.location to '/auth/facebook' so that the OAuth2 Facebook process can begin.
After pressing the "Facebook login", I'd expect the user to be created in the NeDB DB and a valid JWT to be stored so that the feathersclient.authenticate() call would not fail.
But instead of that, the Facebook login page is properly called, and after that the browser is returned to the main page ('/'), but after that, when the main page is reloaded and the feathersclient.authenticate() is called, the server complains that there isn't any valid JWT token, so authentication fails. Also I cannot see the user created in the NeDB DB, so the supposed user and JWT creation that should be done by the feathers-authentication-oauth2 plugin is not...
I've finally made it work... I was wrongly configuring the Facebook authentication strategy, I've changed it to:
app.configure(oauth2({
name: 'facebook',
successRedirect: '/',
failureRedirect: '/',
Strategy: FacebookStrategy
}));
and now it is working.

Reproducing an ADAL.JS-authenticated request in Postman

I have a .NET Web API and a small vanilla-JS app using ADAL.js, and I've managed to make them talk nicely to each-other and authenticate correctly.
If I console.log the token returned from adalAuthContext.acquireToken() and manually enter it as Authorization: Bearer {{token}} in Postman, I can also get a valid, authenticated, response from my backend.
However, I can't figure out how to configure Postman's built-in OAuth2.0 authentication UI to get me tokens automatically. I have managed to get tokens in several ways, but none of them are accepted by the backend.
How do I configure Postman to get a token the same way the ADAL.js library does?
For completeness, here's some code:
Backend configuration:
public void Configuration(IAppBuilder app)
{
app.UseCors(CorsOptions.AllowAll);
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
TokenValidationParameters = new TokenValidationParameters { ValidAudience = "<app-id>" },
Tenant = "<tenant>",
AuthenticationType = "WebAPI"
});
var config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
app.UseWebApi(config);
}
ADAL.js configuration:
const backendUrl = 'http://localhost:55476';
const backendAppId = '<app-id>';
const authContext = new AuthenticationContext({
clientId: backendAppId,
tenant: '<tenant>',
endpoints: [{ [backendAppId]: backendAppId }],
cacheLocation: 'localStorage'
});
Actually making a request:
authContext.acquireToken(backendAppId, (error, token) => {
// error handling etc omitted
fetch(backendUrl, { headers: { Authorization: `Bearer ${token}` } })
.then(response => response.json())
.then(console.log)
})
So since the Azure AD v1 endpoint is not fully standards-compliant, we have to do things in a slightly weird way.
In Postman:
Select OAuth 2.0 under Authorization
Click Get new access token
Select Implicit for Grant Type
Enter your app's reply URL as the Callback URL
Enter an authorization URL similar to this: https://login.microsoftonline.com/yourtenant.onmicrosoft.com/oauth2/authorize?resource=https%3A%2F%2Fgraph.microsoft.com
Enter your app's application id/client id as the Client Id
Leave the Scope and State empty
Click Request token
If you configured it correctly, you'll get a token and Postman will configure the authorization header for you.
Now about that authorization URL.
Make sure you specify either your AAD tenant id or a verified domain name instead of yourtenant.onmicrosoft.com.
Or you can use common if your app is multi-tenant.
The resource is the most important parameter (and non-standards-compliant).
It tells AAD what API you want an access token for.
In this case I requested a token for MS Graph API, which has a resource URI of https://graph.microsoft.com.
For your own APIs, you can use either their client id or App ID URI.
Here is a screenshot of my settings:

Identity Server 4 Authorization Code Flow with Client Credentials (allowing one client instance deny another)

What I got so far:
In a project I have an authorization server (Identity Server 4), some (let's say two) protected APIs (Api Resource) and some trusted clients (automated, no user interaction) which should access the Identity Server via the backchannel (right?). Imagine the client is a Amazon Fire TV box kind thingy.
According to what I have read so far over the last weeks a suitable flow for this scenario is the OpenID Connect Authorization Code Flow.
clients are trusted (and can maintain a secret)
Authorization Code flow supports refresh tokens (which I want to use)
the client is actually not the resource owner but requires access to the full api resource
What I have in my (theoretical) structure:
I have two API Resources (one resource for each API version)
api.v1
api.v2
I also have two series of my API clients
client.v1 supports only api v1 & should only have access to api.v1 resource
client.v2 supports api v1 & v2 and therefore should have access to both api resources
Identity Server 4 StartUp.cs configuration (so far)
public void ConfigureServices(IServiceCollection services)
{
// configure identity server with in-memory stores, keys, clients and scopes
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryApiResources
(
new List<ApiResource>
{
new ApiResource("api.v1", "API v1"),
new ApiResource("api.v2", "API v2")
}
)
.AddInMemoryClients
(
new List<Client>
{
new Client
{
ClientId = "client.v1",
AllowedGrantTypes = GrantTypes.CodeAndClientCredentials,
AllowAccessTokensViaBrowser = false,
ClientSecrets = { new Secret("secret1".Sha256()) },
AllowedScopes = { "api.v1" }
},
new Client
{
ClientId = "client.v2",
AllowedGrantTypes = GrantTypes.CodeAndClientCredentials,
AllowAccessTokensViaBrowser = false,
ClientSecrets = { new Secret("secret2".Sha256()) },
AllowedScopes = { "api.v1", "api.v2" }
}
}
);
}
The theory what I am struggling with is the authorization code part.
I want to have each client instance (again imagine it as a small box) a different authorization code allowing one instance access but deny fo another one.
Is the authorization code intended to be used for that?
And one important thing I haven't understood in all the time: CodeAndClientCredentials defines two grant types. Does this mean connecting with that requires both (code AND client credentials) or is it an one of them definition (code OR client credentials).
The Identity Server 4 code I am struggling with is:
In the code defining the client I can only find AuthorizationCodeLifetime but no field to set the authorization code itself.
It seems I can define a list of client secrets.
ClientSecrets = { new Secret("secret1".Sha256()) },
Does this mean one client Id can have multiple secrets used? Are different client secrets better suitable for my "allow one deny the other" problem?
Edit
Ok, I have re-read that and now I got it (at least a bit more): the authorization code is not defined sent by the client but the client receives it.
The authorization code flow returns an authorization code (like it says on the tin) that can then be exchanged for an identity token and/or access token. This requires client authentication using a client id and secret to retrieve the tokens from the back end
from this blog here
But how would I have to configure my Identity Server to allow one instance and deny another.
By using different client secrets? Using extension grants?

jwt authentication in iOS client nodejs server via third party authenticator

I am trying to wrap my head around using json webtoken (jwt) based authentication on a server coupled to using a third party (say google) to authenticate the user. Originally I've managed to build my own login and jwt handling scheme with jsonwebtoken on my nodejs server, but we need a client running on an iOS system to interact with it and started looking around for a solution where we don't have to code so much client code (requesting new token when expired etc.) and thought that we would use a third party library to do this for us.
The thing is I did not find anything that would do this for us. I found libraries that could handle connecting the client to a google api for the client, I found user identification handled by google, but didn't find anything that would handle actually getting a jwt that the server would except as a genuine user.
My question is essentially this: we have an iOS client and a nodejs server and would like to use google to authenticate our users and have the client call api-s on our nodejs server, with as much of the authentication process handled by some third party library (google's?), how should we get around to this?
As a note, I've seen passport but that seems to operate with sessions only, and I would have to solve the jwt handling by myself were I to use that.
The iOS part is not ready, but I managed to use google to authenticate and authorize without a session in the browser. The idea is, that the client logs in to google (see here for web app) and google graciously also gives you a token with the login, which will be good for the server. On the nodejs side I used passport and the google-id-token strategy (see on github). There are quite a few strategies for google out there, but this one works. Although, this has a shortcoming, it can't accept the token in the header, but I fixed that in a pull request (see here).
Since I had a bit of a problem of how to use the User.findOrCreate part of all the passport examples, I'll put in my code here that covers a full working example:
var passport = require('passport');
var GoogleTokenStrategy = require(passport-google-id-token)
passport.use(new GoogleTokenStrategy({
clientID: config.googleAuth.clientID,
clientSecret: config.googleAuth.clientSecret,
},
function(parsedToken, googleId, done) {
console.log(parsedToken);
console.log(googleId);
User.findOne({ 'google.id': googleId }, function (err, user) {
if (!user) {
var testuser = new User({
name: parsedToken.payload.name,
givenName : parsedToken.payload.givenName,
familyName : parsedToken.payload.familyName,
nameunderscore : parsedToken.payload.name.split(' ').join("_"),
admin: false,
email: parsedToken.payload.email,
settings: {save_folder:"default"},
'google.id' : googleId,
'google.email' : parsedToken.payload.email,
});
testuser.save(function(err) {})
}
return done(err, user);
});
}
));
User comes from mongodb in a separate js:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
module.exports = mongoose.model('User', new Schema({
name: String,
nameunderscore : String,
givenName: String,
familyName: String,
admin: Boolean,
settings: {
save_folder: String
},
email: String,
google: {
id: String,
email: String
}
}));
And this is how I added the passport strategy to a router (note that session is set to false):
var apiRoutes = express.Router();
apiRoutes.use(passport.authenticate('google-id-token',{ session: false }));
Now every call to any route in apiRoutes must send on id_token with a valid google token to get access.

Resources