Yes definitely, this would involve a two-step authentication flow, the base way would be to use Redirect Rules to handle this. I am documenting the steps for that below, Please note that these steps apply to a multi-step signup where you want to add any extra information to app_metadata
etc to the user.
-
If you want to user to be able to login with the username and password,
-
User signs up with social connection, after checking
email
andemail_verified
. You can check if the user has a connected identity with a database user inuser.identities
array. If such an identity exists, the rule will pass through if not, the rule will redirect the user to a special page which asks the user for their username and a password. -
On this page, you can setup your API server so that it accepts, a username-password pair from this page and creates a secondary identity for that user using Management API v2. The page will then redirect the user back to the
/continue
endpoint and when #1> occurs it lets the user pass through.
-
-
If you simply want the user to have a username created for the profile
- Step 1 will remain the same, However, the page will simply ask for a username.
- Step 2 will simply add the username to
app_metadata.username
, in this case, you’ll need to fetch if any users have the same username in order to maintain the uniqueness with Management API v2 Search User and then add app_metadata via the update user command.
The following is the example case 1, where you would want the user to be able to optionally signup with a username and password, However, the key concepts will stay the same with app_metadata
.
function (user, context, callback) {
if (context.protocol === 'redirect-callback') {
if (context.request.query.error) {
return callback(new UnauthorizedError("There was a problem signing you up"));
}
}
function hasDatabaseIdentity (identities) {
return identities.filter(function (identity) {
identity.connection === 'DATABASE_CONNECTION_ID'
}).length > 0;
}
if (hasDatabaseIdentity(user.identities) === false) {
const jwt = require('jsonwebtoken');
const token = jwt.sign({
user_id: user.user_id,
email: user.email,
email_verified: user.email_verified
}, configuration.sharedToken, {
audience: 'https://your_server_route.com/create-identity',
issuer: 'auth0/rule'
});
context.redirect = {
url: `https://your_server_route.com/create-identity?token=${token}`
}
}
return callback(null, user, context);
}
On your client side code you can use a client side framework to build a form like this,
<form method="POST" action="https://your_server_route.com/create-identity?token=${token}&state=${state}">
<input placeholder="username" />
<input type="password" placeholder="password" />
<input type="submit" value="Button"/>
</form>
On the server side, It should be some thing like this (I’m using NodeJS for the example)
app.get('create-identity', async function (req, res) {
res.status(200).end(`
<form method="POST" action="https://your_server_route.com/create-identity?token=${req.query.token}&state=${req.query.state}">
<input name="username" id="username" placeholder="username" />
<input name="password" id="password" type="password" placeholder="password" />
<input type="submit" value="Button"/>
</form>
`);
});
// You should be using the express-jwt middleware.
const jwtMiddleware = expressJwt({
secret: configuration.sharedToken,
audience: 'https://your_server_route.com/create-identity',
issuer: 'auth0/rule',
getToken (req) {
return req.query.token || null;
}
});
// auth0 variable is an Auth0 management api instance from `node-auth0` package
// https://github.com/auth0/node-auth0/
app.post('create-identity', jwtMiddleware, async function (req, res) {
const {username, password} = req.body;
const {user_id, email, email_verified} = req.user;
try{
const createdUser = await auth0.users.create({
connection: 'DATABASE_CONNECTION_ID',
email_verified,
username,
password,
email,
});
await auth0.users.link(user_id, {
connection: 'DATABASE_CONNECTION_ID',
user_id: createdUser.user_id,
provider: 'auth0'
});
res.redirect('https://your-domain.auth0.com/continue' + '?state=' + req.query.state);
}catch(e){
res.redirect('https://your-domain.auth0.com/continue?error=CANT_CREATE&state=' + req.query.state);
}
});