AWS cognito: Auto login after registration confirmation - amazon-cognito

I am using the JavaScript SDK of AWS Cognito (http://docs.aws.amazon.com/cognito/latest/developerguide/using-amazon-cognito-user-identity-pools-javascript-examples.html).
When a new user completes registration confirmation, the documentation says the user is now ready to sign in. Is it possible to automatically sign in the user at this time?
For eg., after confirmation when I use the following I get null:
userPool.getCurrentUser();
If this is the intended behavior, are there any ways to sign in the user without explicitly asking the user again?
I know this is not a good idea, one thing I can think of is to save the user credentials in local storage and use them after confirmation to automatically sign in. Any other ideas better than this?

Upon user signup, your backend will be receiving users credentials, which you can use to generate the JWT token. Then you can add the JWT token in the same response, which can be use by the browser client to request authorized endpoints.
Example:
AWSCognito.config.region = 'us-east-1'; //This is required to derive the endpoint
var poolData = {
UserPoolId: 'us-east-1_TcoKGbf7n',
ClientId: '4pe2usejqcdmhi0a25jp4b5sh3'
};
var userPool = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserPool(poolData);
var attributeList = [];
var dataEmail = {
Name: 'email',
Value: 'email#mydomain.com'
};
var authenticationData = {
Username: 'username',
Password: 'password',
};
var attributeEmail = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserAttribute(dataEmail);
attributeList.push(attributeEmail);
userPool.signUp(authenticationData.Username, authenticationData.Password, attributeList, null, function (err, result) {
if (err) {
alert(err);
return;
}
var authenticationDetails = new AWSCognito.CognitoIdentityServiceProvider.AuthenticationDetails(authenticationData);
var userData = {
Username: authenticationData.Username,
Pool: userPool
};
var cognitoUser = new AWSCognito.CognitoIdentityServiceProvider.CognitoUser(userData);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function (result) {
console.log('access token + ' + result.getAccessToken().getJwtToken());
/*Use the idToken for Logins Map when Federating User Pools with Cognito Identity or when passing through an Authorization Header to an API Gateway Authorizer*/
console.log('idToken + ' + result.idToken.jwtToken);
/*Return the result.idToken.jwtToken with the response*/
},
onFailure: function (err) {
alert(err);
},
});
});

Related

Is NextAuth Credentials safe?

I use next-auth Credentials (v3) to allow my users to register and sign in with good old email and password in my NextJS website. I use MongoDB as my database.
This is my [...nextauth].js file:
export default NextAuth({
session: {
jwt: true
},
providers: [
Providers.Credentials({
async authorize(credentials) {
await dbConnect();
// Check if a user with the email exists
const user = await UserModel.findOne({ email: credentials.email });
if (!user) throw new Error("Emailen is not in use");
// Check if the password is correct
const correctPassword = await bcrypt.compare(
credentials.password,
user.password
);
if (!correctPassword) throw new Error("Wrong password");
return {
userid: user._id,
email: user.email,
};
},
}),
],
callbacks: {
// Add userid to token
async jwt(token, user, account, profile, isNewUser) {
if (user) {
token.id = user.userid;
}
return token
},
// Add userid to session returned to front-end
async session(session, token) {
session.user.id = token.id;
return session
}
}
});
Before fetching data in my NextJS API endpoints, I check if the user is authenticated like this:
const session = await getSession({ req });
const user = await UserModel.findById(session?.user?.id);
if (!session || !user)
return res.status(400).json({ success: false });
But I'm worried that if a person gets the id of another user, they can just edit their JWT session.user.id and access any API endpoint pretending to be another user?
Is that true? Would the users be able to fake their id's in my code?
If so, what can I do to avoid that?

How to get currently login user with passport-jwt

I implemented passport-jwt to authenticate user on protected route and also i want to check maybe the user login before creating first admin, please help me on how to do it.
this is my passport-jwt code that i have implemented
exports.getToken = function (user) {
return jwt.sign(user, config.secretKey, { expiresIn: 3600 });
};
var opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = config.secretKey;
exports.jwtPassport = passport.use(
new JwtStrategy(opts, (jwt_payload, done) => {
console.log("JWT payload: ", jwt_payload);
User.findOne({ _id: jwt_payload._id, }, (err, user) => {
if (err) {
return done(err, false);
} else if (user) {
return done(null, user);
} else {
return done(null, false);
}
});
})
);
If I understand your question correctly, you have authenticated a user and (s)he's logged in. Now, before creating an admin, you want to check if the currently logged in user hasn't expired or something else. Right ?
To do that:
You need to store JWT on the client-side so that whenever you call your API, you can attach the JWT in your request's authentication header. I say Authentication header because your ExtractJWT Strategy is fromAuthHeaderAsBearerToken.
With this you can attach your token to subsequent API calls headers. You also need to implement a middleware on your server-side so that the controller can verify whether the JWT in the Authorization header is valid or invalid.
Here is a good resource to understand the pipeline. Note that in this resource, they fromUrlQueryParameter as the extract strategy, but the concept is the same.

auth0 checkSession({}) returns login_required when logged in through social provider, but not when logging in via username/password

I have an Angular app that uses Auth0 for authentication, and I'm trying to use checkSession({}, …) to persist a user's session if the token hasn't expired yet.
When I log in with my username/pw that I set up for the site, this works fine when I reload the browser/navigate directly to a resource. However, when I log in using a social provider (such as Google), the checkSession({}, …) call on a page reload returns an error and forces the user to log in again.
Some of the relevant code (mostly from the auth0 tutorial(s)):
export class AuthService {
// Create Auth0 web auth instance
private _auth0 = new auth0.WebAuth({
clientID: AUTH_CONFIG.CLIENT_ID,
domain: AUTH_CONFIG.CLIENT_DOMAIN,
responseType: 'token',
redirectUri: AUTH_CONFIG.REDIRECT,
audience: AUTH_CONFIG.AUDIENCE,
scope: AUTH_CONFIG.SCOPE
});
accessToken: string;
userProfile: any;
expiresAt: number;
// Create a stream of logged in status to communicate throughout app
loggedIn: boolean;
loggedIn$ = new BehaviorSubject<boolean>(this.loggedIn);
loggingIn: boolean;
isAdmin: boolean;
// Subscribe to token expiration stream
refreshSub: Subscription;
constructor(private router: Router) {
// If app auth token is not expired, request new token
if (JSON.parse(localStorage.getItem('expires_at')) > Date.now()) {
this.renewToken();
}
}
...
handleAuth() {
// When Auth0 hash parsed, get profile
this._auth0.parseHash((err, authResult) => {
if (authResult && authResult.accessToken) {
window.location.hash = '';
this._getProfile(authResult);
} else if (err) {
this._clearRedirect();
this.router.navigate(['/']);
console.error(`Error authenticating: ${err.error}`);
}
this.router.navigate(['/']);
});
}
private _getProfile(authResult) {
this.loggingIn = true;
// Use access token to retrieve user's profile and set session
this._auth0.client.userInfo(authResult.accessToken, (err, profile) => {
if (profile) {
this._setSession(authResult, profile);
this._redirect();
} else if (err) {
console.warn(`Error retrieving profile: ${err.error}`);
}
});
}
private _setSession(authResult, profile?) {
this.expiresAt = (authResult.expiresIn * 1000) + Date.now();
// Store expiration in local storage to access in constructor
localStorage.setItem('expires_at', JSON.stringify(this.expiresAt));
this.accessToken = authResult.accessToken;
this.userProfile = profile;
if (profile) {
this.isAdmin = this._checkAdmin(profile);
}
...
}
...
get tokenValid(): boolean {
// Check if current time is past access token's expiration
return Date.now() < JSON.parse(localStorage.getItem('expires_at'));
}
renewToken() {
// Check for valid Auth0 session
this._auth0.checkSession({}, (err, authResult) => {
if (authResult && authResult.accessToken) {
this._getProfile(authResult);
} else {
this._clearExpiration();
}
});
}
}
(This is from a service that is called in many places within the app, including some route guards and within some components that rely on profile information. If more of the app code would be useful, I can provide it.)
Also note: AUTH_CONFIG.SCOPE = 'openid profile email'
So, the issue appears to not have been related to my app at all. When using Social Providers, Auth0 has an explicit note in one of their tutorials that really helped me out:
The issue with social providers is that they were incorrectly configured in my Auth0 dashboard, and needed to use provider-specific app keys.
Important Note: If you are using Auth0 social connections in your app,
please make sure that you have set the connections up to use your own
client app keys. If you're using Auth0 dev keys, token renewal will
always return login_required. Each social connection's details has a
link with explicit instructions on how to acquire your own key for the
particular IdP.
Comment was found on this page: https://auth0.com/blog/real-world-angular-series-part-7/

Retrieve id token after authentication with Auth0's Lock

I am using Auth0's Lock widget to get users authenticated.
How do I go about getting an idToken once that's done?
The idToken is within the authResult parameter available on authenticated event if your Lock's options specify id_token in the auth.responseType option:
var lock = new Auth0Lock(
'<your-client-id>',
'<your-auth0-domain>',
{
auth: {
responseType: 'token id_token',
},
container: 'lockRoot'
}
);
lock.on("authenticated", function(authResult) {
console.log('id token: ' + authResult.idToken);
}

AWS Cognito Authentication via Facebook succeeds but getCurrentUser returns null

In the browser, after Facebook Login, statusChangeCallback is called. Everything succeeds. Cognito even returns an Identity Id. However, userPool.getCurrentUser() returns null. Cognito does not think there is an authenticated user. How can I fix that? Thanks.
function statusChangeCallback(response) {
if(response.status == 'connected' && response.authResponse) {
testAPI()
console.log("FB statusChangeCallback", JSON.stringify(response))
AWSCognito.config.credentials = new AWSCognito.CognitoIdentityCredentials({
IdentityPoolId : '<%=process.env.AWS_USERPOOLGUID%>', // your identity pool id here
Logins : {
'graph.facebook.com': response.authResponse.accessToken
}
});
console.log(JSON.stringify(AWSCognito.config.credentials))
AWSCognito.config.region = '<%= process.env.AWS_REGION%>'
AWSCognito.config.credentials.refresh(function(error) {
if (error) {
console.error("AWSCognito.config.credentials.get", error);
} else {
console.log("Cognito Identity Id", AWSCognito.config.credentials.identityId);
console.log('Successfully logged!');
var cognitoUser = userPool.getCurrentUser();
console.log('cognitoUser', cognitoUser);
}
});
}
}
userPool.getCurrentUser();
refers to the authenticated user with regards to the particular user pool. What you are doing, in the above code is obtaining AWS credentials using a Facebook identity. However, the current user refers to the last authenticated user of the user pool. That is saved in local storage after a successful authentication. So you would need to authenticate first, similar to the code below.
var authenticationData = {
Username : 'username',
Password : 'password',
};
var authenticationDetails = new AWSCognito.CognitoIdentityServiceProvider.AuthenticationDetails(authenticationData);
var poolData = {
UserPoolId : '...', // Your user pool id here
ClientId : '...' // Your client id here
};
var userPool = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserPool(poolData);
var userData = {
Username : 'username',
Pool : userPool
};
var cognitoUser = new AWSCognito.CognitoIdentityServiceProvider.CognitoUser(userData);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function (result) {
console.log('access token + ' + result.getAccessToken().getJwtToken());
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId : '...', // your identity pool id here
Logins : {
// Change the key below according to the specific region your user pool is in.
'cognito-idp.<region>.amazonaws.com/<YOUR_USER_POOL_ID>' : result.getIdToken().getJwtToken()
}
});
// Instantiate aws sdk service objects now that the credentials have been updated.
// example: var s3 = new AWS.S3();
},
onFailure: function(err) {
alert(err);
},
});
Looks like you need to change your AWSCognito.config.credentials
From what you have to this:
// Add the Facebook access token to the Cognito credentials login map.
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'IDENTITY_POOL_ID',
Logins: {
'graph.facebook.com': response.authResponse.accessToken
}
});
// Obtain AWS credentials
AWS.config.credentials.get(function(){
// Access AWS resources here.
});
NOTICE : IdentityPoolId: 'IDENTITY_POOL_ID', and not IdentityPoolId : '<%=process.env.AWS_USERPOOLGUID%>', // your identity pool id here
Looks like you are trying to access your USER POOL and not your IDENTITY POOL.
Facebook users live in the Identity Pool because they are a federated user from the Facebook server.