This is my first post, so excuse me if I make any newbie mistakes.
I found Sign-up flow 1 to work quite well. You didnt specify which technologes you were using, but here is a link to my github where I have a fully functioning blog using Sign-up flow 1 with React, redux and an Express backend.
https://github.com/iqbal125/react-redux-fullstack-blog
I will demonstrate with these frameworks so hopefully you can adjust your code for which ever framework(s) you are using.
My signup process is as follows:
- Frontend shows the Lock user signs up
- User is redirected to callback page.
- I then redirect from the callback page to an "auth-check" page. I have a nested api call in auth-check page that both gets the user data from auth0 then immediately calls api endpoint to save user data to db.
- The api call checks if user is already in sql db then saves the user data, otherwise does nothing.
- The user data is then saved to redux global state and can be used to display data on the user profile page.
- When user clicks logout, authcheck is called again and user info is removed from global state and then user is logged out.
- auth-check then redirects back to homepage.
1. Frontend shows the Lock user signs up
login() {
this.auth0.authorize();
}
2. User is redirected to callback page.
My callback page is very simple and I it use as a functional component.
<div>
<h2>Callback</h2>
</div>
3. I then redirect from the callback page to an "auth-check" page
I do this through the handleAuthentication() function in my auth.js util component. The code is slightly modified from the auth0 samples.
handleAuthentication() {
this.auth0.parseHash((err, authResult) => {
if (authResult && authResult.accessToken && authResult.idToken) {
this.setSession(authResult);
this.getProfile();
setTimeout( function() { history.replace('/authcheck') }, 2000);
} else if (err) {
history.replace('/');
console.log(err);
alert(`Error: ${err.error}. Check the console for further details.`);
}
});
}
You will notice I added a getProfile() function
getProfile() {
let accessToken = this.getAccessToken();
if(accessToken) {
this.auth0.client.userInfo(accessToken, (err, profile) => {
if (profile) {
this.userProfile = { profile };
}
});
}
}
along with a getAccessToken() function
getAccessToken() {
if (localStorage.getItem('access_token')) {
const accessToken = localStorage.getItem('access_token')
return accessToken
}
else {
console.log("No accessToken")
return null
}
}
These two functions in the auth.js util component will be what allow us to get the info from auth0 and save it to an empty object we declared in the class.
userProfile = {}
Moving on to the auth-check.js container. I start by declaring the function in the constructor and followed by the function itself. Then I call the componentDidMount() lifecycle method which runs automatically when the component renders.
constructor() {
super()
this.send_profile_to_db = this.send_profile_to_db.bind(this)
}
send_profile_to_db (profile) {
const data = profile
axios.post('api/post/userprofiletodb', data)
.then(() => axios.get('api/get/userprofilefromdb', {params: {email: profile.profile.email}} )
.then(res => this.props.db_profile_success(res.data))
.then(history.replace('/')))
}
My lifecycle method and Im returning an empty div.
componentDidMount() {
if(this.props.auth.isAuthenticated()) {
this.props.login_success()
this.props.db_profile_success(this.props.auth.userProfile)
this.send_profile_to_db(this.props.auth.userProfile)
} else {
this.props.login_failure()
this.props.profile_failure()
this.props.db_profile_failure()
history.replace('/')
}
}
render() {
return (
<div>
</div>
)
}
}
I think this code right here gets to the heart of the question you asked.
I will start with the send_profile_to_db() function.
Here im using axios to make requests. I start my making a backend api call to my express server(I will explain in the next step) and Im passing the user profile as a data object parameter with axios. You might be wondering where the actual user profile data is coming from.
In my routes.js root component I imported and initialized a new instance of Auth
export const auth = new Auth();
then passed it as a prop to the AuthCheck component.
<Route path="/authcheck" render={(props) => <AuthCheck auth={auth} {...props} />} />
This allows me to access all the properties of the auth class with "this.props". So I simply use the "userProfile = {}" object that we initialized in the last step that now contains our user data.
After posting the data to the database Im using a nested ".then()" function that calls an axios get request with the users email as a parameter for looking up the profile from the database. The database profile contains data about the user's posts and user's comments. Which will be useful for the displaying data in the app. Then Im using another ".then()" statement and a Redux Thunk to save the user profile data to the global redux state asynchronously.
So in sum this authcheck component is doing 4 things:
1. Saving the user profile data we get from auth0 to our own database.
2. Then after saving the data, immediately retrieving the same profile from our database.
3. Making our app aware whether the user is authenticated or not.
4. Saving our database user profile data to the global redux state for use in other components.
Pretty awesome, if you ask me!
4. The api call checks if user is already in sql db then saves the user data, otherwise does nothing.
Now here is my server set up. For the user to database "post" and "get" requests.
router.post('/api/post/userprofiletodb', (req, res, next) => {
const values = [req.body.profile.nickname, req.body.profile.email, req.body.profile.email_verified]
pool.query('INSERT INTO users(username, email, date_created, email_verified) VALUES($1, $2, NOW(), $3) ON CONFLICT DO NOTHING', values, (q_err, q_res) => {
if (q_err) return next(q_err);
console.log(q_res)
res.json(q_res.rows);
});
});
/* Retrieve user profile from db */
router.get('/api/get/userprofilefromdb', (req, res, next) => {
// const email = [ "%" + req.query.email + "%"]
const email = String(req.query.email)
pool.query("SELECT * FROM users WHERE email = $1", [ email ], (q_err, q_res) => {
res.json(q_res.rows)
});
});
A few things to note:
the router object is express.router(). Im using psql.
Remember to add "ON CONFLICT DO NOTHING" otherwise you will save multiple versions of the same user.
I think there is a couple more data points that auth0 gives you but I ended up not using them.
Here is my SQL schema for users table.
CREATE TABLE users (
uid SERIAL PRIMARY KEY,
username VARCHAR(255) UNIQUE,
email VARCHAR(255),
email_verified BOOLEAN,
date_created DATE,
last_login DATE
);
5. The user data is then saved to redux global state and can be used to display data on the user profile page.
I ended up just explaining this in step 3.
6. When user clicks logout, authcheck is called again and user info is removed from global state and then user is logged out.
see step 3
7. auth-check then redirects back to homepage.
Once again see step 3 lol.
Be sure to check out my repo if youre interested or if I missed anything, like I said its a complete fully functioning blog.