I am using Auth0 to host all my user data. I also have my own backend, and I wish to have a Users
table in it, which will map my db's generated userId
to Auth0's user_id
. I am hesitating between two flows on sign-up:
user_id
.POST /users
(public endpoint) to create a new user with user_id
.user_id
, so the db makes a lookup between the user_id
and my userId
.POST /users
on my backend. This call will generate my db's userId
and send it back to Auth0.userId
into Auth0's user_metadata
.user_metadata
will be included in the JWT, so that all calls to my backend to fetch resources will include the db's userId
(no need for additional lookup).I feel 2 is more solid. Are there other sign-up flows? Do some auth0 customers use a similar flow to my #2? I didn't find much in their documentation.
Auth0 stores user information for your tenant in a hosted cloud database, or you can choose to store user data in your own custom external database. To store user data beyond the basic information Auth0 uses for authentication, you can use the Auth0 data store or a custom database.
Navigate to Auth0 Dashboard > Authentication > Database, and select Create DB Connection. Enter a name for your connection, and select Create. Select the Applications view, enable the switch for each Auth0 application that should be able to use this connection.
Auth0 helps you prevent critical identity data from falling into the wrong hands. We never store passwords in cleartext. Passwords are always hashed and salted using bcrypt. Additionally, data at rest and in motion is always encrypted by using TLS with at least 128-bit AES encryption.
Auth0 is a flexible, drop-in solution to add authentication and authorization services to your applications. Your team and organization can avoid the cost, time, and risk that come with building your own solution to authenticate and authorize users.
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:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With