问题
I'm using the Amplify authenticator component (described here) with Cognito User Pools.
Right now, adding the basic authenticator to html takes the user through the following process automatically:
1) Sign up
2) Enter verification code sent to email
3) Sign-in: Re-enter user name and password
This is based on just adding to html:
<amplify-authenticator></amplify-authenticator>
So new users sign up, and then right away need to sign in. It would be better if they were automatically signed-in, so that upon entering their verification code they went right into the app. This would be a common authentication flow.
Is there a way to have automatic sign-in like this while still using the authenticator components?
I see a github discussion about this topic here, but it is not resolved.
回答1:
I have figured it out for my purposes. The key is that the Amplify components (authenticator, as well as the individual components) give out information about the state after the user takes action. You can listen for this info, and then programmatically sign in the user once you get the "signIn" state.
These are the states, as described here (I am using Angular):
'signUp' //when you want to show signUp page
'confirmSignUp' //the user has probably just submitted info for signing up, and now you want to show the "confirm" page--like the page where they can enter a verification code
'signIn' //this is the key for these purposes. This is when you want to show the sign in page. In the basic authenticator structure, this happens just after the user has been signed up. So this is where you can automatically sign in the user with Auth.signIn, like below.
'confirmSignIn' //I assume this is after user has submitted the sign in form and you want a confirm page to show
'signedIn' //presumably after user has been fully signed in (not confirmed)
'forgotPassword' //presumably when the forgotPassword form loads (not confirmed)
'requireNewPassword' //presumably when user clicks 'reset password' (not confirmed)
So, using Authenticator, you can listen for the 'signIn' event and automatically sign in the user then. Note however that, while this will sign in the user automatically, Authenticator may still show the sign in page while this action is being processed. You can hide this page, or just use the individual Amplify components, like I show in #2 below.
Even better would be if Amplify/Cognito could just have an option for the developer to select automatic sign in after sign up when they set up Cognito. Automatic sign in on sign up would be in line with modern authentication practice, and would be better for user flow. I do not find this option anywhere. If anyone at Amplify/Cognito is listening, please consider adding this feature.
Note: There seem to be some issues that can arise when using these components. For example--with the authenticator, if the user submits info to sign up, but then leaves the page before entering their verification code: the next time they try to access the app they may have trouble, because they can't go through the sign up process again (a user has already been created), but they also can't sign in (because they've never verified). Some more discussion on that is here.
Additionally, I have found error messages to be an issue. These Amplify components automatically show error messages, and some of them from Cognito are technical messages I would never want a user to see (stuff that talks about "user id" and lambda functions, depending on the random error). There may be a way to customize these, like described here, but be sure you test a lot of different scenarios to see what might happen in terms of the error messages.
If these issues prove problematic for you (like they have for me), you may want to use your own forms instead, and then use Auth.signIn() and related Amplify methods, instead of these components.
But, for auto-signing-in with Amplify components, here is code that worked for me:
1. Using Amplify's Authenticator component:
html
<amplify-authenticator></amplify-authenticator>
ts:
import { AmplifyService } from 'aws-amplify-angular';
import Auth from '@aws-amplify/auth';
Export class AuthComponent implements OnInit {
state: any;
newUser: any;
username: any;
password: any;
constructor(public amplifyService: AmplifyService){
this.amplifyService.setAuthState(this.authState) //may be not required
this.amplifyService.authStateChange$ //listening for state changes
.subscribe(authState => {
this.state = authState.state
if (this.state === 'confirmSignUp'){
console.log('user just signed up, now on verify code form')
this.newUser = authState.user
this.password = this.newUser.username
let checkPassword = this.newUser.username.password
if (checkPassword != 'undefined') {
this.password = checkPassword
/*Note here: I have coded it this way because it looks like the authenticator runs two events when the user hits "sign up". In the first event, you can get the user's password, to be used in the Auth.signIn() function below. In the second event, you can only get the user's username (and password would show up as undefined if you grabbed it there). So we need a way to get the password from the first event.*/
}
}
if ((this.newUser) && (this.state === 'signIn')){//this.newUser included because otherwise this event will fire anytime the sign in page loads--even for returning users trying to sign in (who you would not want to sign in automatically)
console.log('user has just finished signing up)
Auth.signIn(this.username, this.password).then(()=>{//there might be more parameters, like email, first name and last name, phone number, etc. here--depends on your Cognito settings
console.log('should be signed in now! You can navigate away from this page')
}).catch((error)=>{
console.log('error here = ' + error.message + ', error code = ' + error.code)
})
}
})
}
}
Like I mentioned above, this will show the "sign in" page while the Auth.signIn() function is processing. To avoid that, you could have an *ngIf, saying hide the page when this.state = "signIn".
2. Using Individual Amplify Auth Components:
In the below, the page will load with the sign up form.
Once the user enters their details and clicks sign up, the page will show the "confirm sign up" form, which is where the user enters a verification code that Cognito has sent to him/her (depending on your Cognito settings).
Then, once the user is signed up, you can get the "signIn" state like above, and automatically sign the user in:
html:
<amplify-auth-sign-up [authState]="authState" *ngIf="showSignUp"></amplify-auth-sign-up>
<amplify-auth-confirm-sign-up [authState]="authState" *ngIf="showVerify"></amplify-auth-confirm-sign-up>
ts:
import { AmplifyService } from 'aws-amplify-angular';
import { AuthState } from 'aws-amplify-angular/dist/src/providers';
import Auth from '@aws-amplify/auth';
Export class AuthComponent implements OnInit {
public authState: AuthState
public newUser: any
public username: any
public password: any
public state: any
public showSignUp = true
public showVerify = false
constructor(public amplifyService: AmplifyService) {
this.authState ={//the individual Amplify components require a state be set
user: null,
state: 'signUp'
}
this.amplifyService.setAuthState(this.authState) //this might not be required
this.amplifyService.authStateChange$ //listening for state changes.
.subscribe(authState => {
this.state = authState.state
if (this.state === 'confirmSignUp'){//get change in state
this.newUser = authState.user
this.username = this.newUser.username
let checkPassword = this.newUser.username.password
if (checkPassword != 'undefined') {
this.password = checkPassword
/*Note here: I have coded it this way because it looks like the authenticator runs two events when the user hits "sign up". In the first event, you can get the user's password, to be used in the Auth.signIn() function below. In the second event, you can only get the user's username (and password would show up as undefined if you grabbed it there). So we need a way to get the password from the first event.*/
}
this.authState ={
user: authState.user,
state: 'confirmSignUp'
}
this.showSignUp = false
this.showVerify = true
}
if ((this.newUser) && (this.state === 'signIn')){//this.newUser included because otherwise this event will fire anytime the sign in page loads--even for returning users trying to sign in (who you would not want to sign in automatically)
console.log('user has just finished signing up)
Auth.signIn(this.username, this.password).then(()=>{//there might be more parameters, like email, first name and last name, phone number, etc. here--depends on your Cognito settings
console.log('should be signed in now! You can navigate away from this page')
}).catch((error)=>{
console.log('error here = ' + error.message + ', error code = ' + error.code)
})
}
})
}
}
来源:https://stackoverflow.com/questions/59254358/auto-sign-in-with-cognito-amplify-authenticator-components