What to use as password when registering a user via Google OAuth? - authentication

I have a sign-up page that uses email and password.
I store the email and (salted, hashed) password in the database.
I'm now adding Google OAuth to my sign-up page.
For the main advantage being that user now shouldn't have to pick a password.
When the user authenticates and I receive the user's Google profile, what exactly do use now as their "password"? The profile object looks like this:
{
Ca: '123456789...',
wc: {
token_type: 'Bearer',
access_token: 'abc.123.ABCabc..........',
scope: 'email profile ...',
login_hint: '...',
expires_in: 3599,
id_token: '......',
session_state: { ... },
first_issued_at: ...,
expires_at: ...,
...
},
googleId: ...,
tokenObj: { ... },
profileObj: { googleId: ..., name: ..., },
}
I was inclined to use "access_token" or "id_token" as password but then there's "expires_at" field that suggests they might change over time and be of no use in future login attempts.
Would they? If so, what else should I choose as the user's password that won't change and allow to me authenticate them every time they login via Google OAuth?

Related

OIDC-react signtOut doesn't end Cognito session

My stack: oidc-react, Amazon Cognito
When I log out on the site and call auth.signOut();, the userManager signs out the user and redirects to the login page, but when you log in again by calling auth.signIn(); makes a request to Cognito with the token it has, but won't ask for credentials and logs the user in, I guess because the user still has a running session with the same token, if I am right. It only asks for login credentials after an hour because the session expires after 60minutes.
I want Congito to ask for credentials after signing out. I've tried passing the config these options after some research, but doesn't seem to be working:
revokeTokenTypes: (["access_token", "refresh_token"]),
revokeTokensOnSignout: true,
automaticSilentRenew: false,
monitorSession: true
This is the OIDC setup I pass to the provider:
const oidcConfig: AuthProviderProps = {
onSignIn: (user: User | null) => {
console.log("onSignIn");
},
onSignOut: (options: AuthProviderSignOutProps | undefined) => {
console.log('onSignOut');
},
autoSignIn: false,
loadUserInfo: true,
postLogoutRedirectUri: "localhost:3000/",
automaticSilentRenew: false,
authority: "https://" + process.env.REACT_APP_AWS_COGNITO_DOMAIN,
clientId: process.env.REACT_APP_AWS_COGNITO_CLIENT_ID,
redirectUri: window.location.origin,
responseType: 'code',
userManager: new UserManager({
authority: "https://" + process.env.REACT_APP_AWS_COGNITO_DOMAIN,
client_id: process.env.REACT_APP_AWS_COGNITO_CLIENT_ID!,
redirect_uri: window.location.origin,
revokeTokenTypes: (["access_token", "refresh_token"]),
revokeTokensOnSignout: true,
automaticSilentRenew: false,
monitorSession: true
})
};
AWS Cognito does not yet implement the RP Initiated Logout specification or return an end_session_endpoint from its OpenID Connect discovery endpoint. I expect this is your problem, since the library is probably implemented in terms of these standards.
Instead, AWS Cognito uses these parameters and a /logout endpoint. In my apps I have implemented Cognito logout by forming a URL like this, then redirecting to it by setting location.href to the URL value:
public buildLogoutUrl(): string {
const logoutReturnUri = encodeURIComponent(this._configuration.postLogoutRedirectUri);
const clientId = encodeURIComponent(this._configuration.clientId);
return `${this._configuration.logoutEndpoint}?client_id=${clientId}&logout_uri=${logoutReturnUri}`;
}
This will enable you to end the Cognito session and force the user to sign in again. It will also enable you to return to a specific location within your app, such as a /loggedout view.

How to include TOTP MFA in AWS Cognito authentication process

I'm using Cognito user pools to authenticate my web application. I've got it all working right now but now I need to enable MFA for it. This is how I do it right now (all the code provided are server-side code):
Signing up the user:
const cognito = new AWS.CognitoIdentityServiceProvider();
cognito.signUp({
ClientId,
Username: email,
Password,
}).promise();
An email is sent to the user's address (mentioned as username in the previous function call) with a code inside.
The user reads the code and provides the code to the next function call:
cognito.confirmSignUp({
ClientId,
ConfirmationCode,
Username: email,
ForceAliasCreation: false,
}).promise();
The user logs in:
const tokens = await cognito.adminInitiateAuth({
AuthFlow: 'ADMIN_NO_SRP_AUTH',
ClientId,
UserPoolId,
AuthParameters: {
'USERNAME': email,
'PASSWORD': password,
},
}).promise();
I'm pretty happy with this process. But now I need to add the TOTP MFA functionality to this. Can someone tell me how these steps will be changed if I want to do so? BTW, I know that TOTP MFA needs to be enabled for the user pool while creating it. I'm just asking about how it affects my sign-up/log-in process.
Alright, I found a way to do this myself. I must say, I couldn't find any documentation on this so, use it at your own risk!
Of course, this process assumes you have a user pool with MFA enabled (I used the TOTP MFA).
Signing up the user:
const cognito = new AWS.CognitoIdentityServiceProvider();
cognito.signUp({
ClientId,
Username: email,
Password,
}).promise();
An email is sent to the user's address (mentioned as username in the previous function call) with a code inside.
The user reads the code and provides the code to the next function call:
cognito.confirmSignUp({
ClientId,
ConfirmationCode: code,
Username: email,
ForceAliasCreation: false,
}).promise();
The first log in:
await cognito.adminInitiateAuth({
AuthFlow: 'ADMIN_NO_SRP_AUTH',
ClientId,
UserPoolId,
AuthParameters: {
'USERNAME': email,
'PASSWORD': password,
},
}).promise();
At this point, the return value will be different (compared to what you'll get if the MFA is not enforced). The return value will be something like:
{
"ChallengeName": "MFA_SETUP",
"Session": "...",
"ChallengeParameters": {
"MFAS_CAN_SETUP": "[\"SOFTWARE_TOKEN_MFA\"]",
"USER_ID_FOR_SRP": "..."
}
}
The returned object is saying that the user needs to follow the MFA_SETUP challenge before they can log in (this happens once per user registration).
Enable the TOTP MFA for the user:
cognito.associateSoftwareToken({
Session,
}).promise();
The previous call is needed because there are two options and by issuing the given call, you are telling Cognito that you want your user to enable TOTP MFA (instead of SMS MFA). The Session input is the one return by the previous function call. Now, this time it will return this value:
{
"SecretCode": "...",
"Session": "..."
}
The user must take the given SecretCode and enter it into an app like "Google Authenticator". Once added, the app will start showing a 6 digit number which is refreshed every minute.
Verify the authenticator app:
cognito.verifySoftwareToken({
UserCode: '123456',
Session,
}).promise()
The Session input will be the string returned in step 5 and UserCode is the 6 digits shown on the authenticator app at the moment. If this is done successfully, you'll get this return value:
{
"Status": "SUCCESS",
"Session": "..."
}
I didn't find any use for the session returned by this object. Now, the sign-up process is completed and the user can log in.
The actual log in (which happens every time the users want to authenticate themselves):
await cognito.adminInitiateAuth({
AuthFlow: 'ADMIN_NO_SRP_AUTH',
ClientId,
UserPoolId,
AuthParameters: {
'USERNAME': email,
'PASSWORD': password,
},
}).promise();
Of course, this was identical to step 4. But its returned value is different:
{
"ChallengeName": "SOFTWARE_TOKEN_MFA",
"Session": "...",
"ChallengeParameters": {
"USER_ID_FOR_SRP": "..."
}
}
This is telling you that in order to complete the login process, you need to follow the SOFTWARE_TOKEN_MFA challenge process.
Complete the login process by providing the MFA:
cognito.adminRespondToAuthChallenge({
ChallengeName: "SOFTWARE_TOKEN_MFA",
ClientId,
UserPoolId,
ChallengeResponses: {
"USERNAME": config.username,
"SOFTWARE_TOKEN_MFA_CODE": mfa,
},
Session,
}).promise()
The Session input is the one returned by step 8 and mfa is the 6 digits that need be read from the authenticator app. Once you call the function, it will return the tokens:
{
"ChallengeParameters": {},
"AuthenticationResult": {
"AccessToken": "...",
"ExpiresIn": 3600,
"TokenType": "Bearer",
"RefreshToken": "...",
"IdToken": "..."
}
}

Programmatically authenticate in Auth0

I'm setting up cypress.io to test a react app. I'm trying to programmatically authenticate with Auth0 Lock so I don't have to test their UI. Here are the steps:
POST to https://[auth0-domain].com/usernamepassword/login
client_id: "",
client_secret: "",
audience: "audience",
redirectUri: "http://[auth0-domain].com:8383/callback",
scope: "openid email crud:all",
protocol: "oauth2",
sso: true,
username: "",
password: "",
connection: "conn",
tenant: "tenant",
popup: false,
Collect returned form data
POST to https://[auth0-domain].com/login/callback
wa=wsignin1.0&wresult=token&wctx=metastuff
The first steps works but in the third step I'm able to post to login/callback but I get an HTML page with this error message:
Looks like something went wrong! There could be a misconfiguration in the system or a service outage. We track these errors automatically, but if the problem persists feel free to contact us with this tracking id: <b>e88e08e43b7cdee78458</b>.<br/>Please try again.
I'm wondering if there is something with Auth0 blocking me from doing this or if I'm not sending the correct data/header.
I ended up using the auth0-js module which calls the auth0 callback. Then I use 'should' to wait for the localStorage to be set:
import auth0 from 'auth0-js';
import url from 'url';
Cypress.Commands.add('login', () => {
const auth = new auth0.WebAuth({
audience: 'http://audience',
domain: 'domain.auth0.com',
clientID: 'qqqqqqqqqqqq',
redirectUri: 'http://localhost/callback',
responseType: 'token id_token',
scope: 'openid email profile name username groups roles',
sso: true
});
auth.login({username: 'username', password: 'pass'});
cy.window().its('localStorage.user_profile').should('exist')
});

Email only authentication with Vue.js and Vuex on Firebase

I want user to be automatically authenticated (temporarily) on Firebase just by sending Email then be redirected to a welcome page asking to complete the auth process by following a link received by email.
The first part is ok, I can authenticate by just inserting email and generating a random password like the following (Vuex store action):
this.$store.dispatch('userSignUp', { email: this.email, password: this.generatePassword })
which is called by component method button v-on:click="userSignUp
Vuex action is like :
userSignUp ({commit}, payload) {
commit('setLoading', true)
firebase.auth().createUserWithEmailAndPassword(payload.email, payload.password)
.then(firebaseUser => {
commit('setUser', firebaseUser)
commit('setLoading', false)
commit('setError', null)
router.push('/welcome')
})
.catch(error => {
commit('setError', error.message)
commit('setLoading', false)
})
}
So far so good, the user put the email, an helper function this.generatePassword generate a random password and the user is logged in and created on firebase.
Now this user is logged in, is on a welcome page, but it doesn't know his own random password (because I don't want to).
I want this to be one shot login and if the user want to come back, has to follow the link sent by email by Firebase.
There is a firebase function [sendPasswordResetEmail][1], which seems right for the case but I connot find the way to make it working.
I did Vuex action like before :
export const actions = {
sendPasswordReset ({commit}, payload) {
commit('setLoading', true)
firebase.auth().sendPasswordResetEmail(payload.email)
.then(firebaseUser => {
commit('setUser', firebaseUser)
commit('setLoading', false)
commit('setError', null)
router.push('/welcome')
})
.catch(error => {
commit('setError', error.message)
commit('setLoading', false)
router.push('/error')
})
},
...
which is called by component method button v-on:click="userSignUp
methods: {
userSignUp () {
this.$store.dispatch('userSignUp', { email: this.email, password: this.generatePassword })
this.$store.dispatch('sendPasswordReset', { email: this.email })
}
},
I only get response code
{
"error": {
"errors": [
{
"domain": "global",
"reason": "invalid",
"message": "EMAIL_NOT_FOUND"
}
],
"code": 400,
"message": "EMAIL_NOT_FOUND"
}
}
while the Request Payload seems ok anyway :
{requestType: "PASSWORD_RESET", email: "luca.soave#gmail.com"}
email
:
"luca.soave#gmail.com"
requestType
:
"PASSWORD_RESET"
Any idea ?
The provider you're using is called the password provider. As its name implies it is heavily dependent on the user having (and knowing) a password. Since you are looking for passwordless authentication, I'd recommend against using the email+password provider as the basis.
Instead consider implementing a custom authentication provider. While this involves a few more components, it is not as difficult as you may think. You'll need to run trusted code, which you can do either on a server you already have, or on Cloud Functions. In either of those cases, you'll use one of the Admin SDKs to implement the sensitive parts of the authentication flow.
A quick list of steps that I think you'll need:
Create an endpoint (e.g. a HTTP triggered Cloud Function) for the user to request an authentication email.
Implement the code for this endpoint to:
Generate a random one-time code in there, which you're going to send to the user. Firebase Authentication calls this the out-of-band (or OOB) code, since it's sent to the user on a different medium than your app.
Store this code and the user's email address somewhere where only your server-side code can read it, e.g. in the Firebase Database or Cloud Firestore.
Send an email to the user, with the code or a link to a page that includes the code and their email address.
Create an endpoint (e.g. again a HTTP function, or web page) where the user enters (e.g. by clicking on a link in the email) the OOB code and their email address.
Compare the code the user entered, to the one you stored before.
If the codes match, generate a custom token for the user and send it to them.
The user/app now signs into Firebase with the custom token.

Can I create an AWS Cognito user login programmatically?

I would like my app to allow users with a Facebook, Google, Amazon... etc... accounts to be able to login to my app. This works fine through AWS Cognito.
However, is there a way for the app to create a user login programmatically if the user does not have any of those logins?
The user would provide an id and a password and the app would send the information to the authentiation provider to create a new login/account.
I would not need to implement my own authentication mechanism and worry about how the passwords are stored, etc.
From my research I take that there is no way to do this with existing authentication providers or even other services such as OpenID.
Do you have any other options if I do not want to implement my own login storage and authentication? It would not necessarily need to integrate with AWS Cognito.
I'm a little confused by your question. If you're asking:
Can I create new usernames and passwords on Facebook / Google programatically?
Then the answer is no. You have to sign up for Facebook / Google on their site. If you're asking:
Can I create a new user with a username and password that only exists in Cognito?
Then the answer is yes. To do this, it depends on whether you're creating the user in a browser or on a server. In a browser, use the Cognito Javascript API. On a server, use the Cognito Admin Server APIs.
Here's some sample code for creating a new user on the server in Node JS (replace my strings with your own tokens, especially the ones with # signs in them):
let params = {
UserPoolId: "#cognito_pool_id#",
Username: "jhancock",
DesiredDeliveryMediums: ["EMAIL"],
ForceAliasCreation: false,
MessageAction: "SUPPRESS",
TemporaryPassword: "somePassword",
UserAttributes: [
{ Name: "given_name", Value: "John"},
{ Name: "family_name", Value: "Hancock"},
{ Name: "name", Value: "John Hancock"},
{ Name: "email", Value: "john#gmail.com"},
{ Name: "phone_number", Value: "+15125551212"}
],
};
console.log("Sending params to cognito: " + JSON.stringify(params));
let cognitoIdentityServiceProvider = new AWS.CognitoIdentityServiceProvider({region: "us-east-1"});
cognitoIdentityServiceProvider.adminCreateUser(params, function(error, data) {
if (error) {
console.log("Error adding user to cognito: " + JSON.stringify(error), error.stack);
} else {
console.log("Received back from cognito: " + JSON.stringify(data));
}
}
One you get that working, you'll probably want to see this post about how to change the temporary password into a real one.
Hi from my previous experence in implementing of the social media authentication.
I would conclude that it is quite hard to implement.If you do not what to show web view to authenticate user in iOS you need to use iOS ACAccountStore class for this, but even this only gives opportunity to log in not to sign in.