Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: document TOTP adapter #934

Open
wants to merge 3 commits into
base: gh-pages
Choose a base branch
from
Open
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 169 additions & 2 deletions _includes/parse-server/third-party-auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ Note, most of them don't require a server configuration so you can use them dire
```

The options passed to Parse Server:

```js
{
auth: {
Expand Down Expand Up @@ -88,6 +89,7 @@ Learn more about [Facebook login](https://developers.facebook.com/docs/authentic
```

The options passed to Parse Server:

```js
{
auth: {
Expand Down Expand Up @@ -202,7 +204,7 @@ The options passed to Parse Server:
{
auth: {
keycloak: {
config: require(`./auth/keycloak.json`) // Required
config: require(`./auth/keycloak.json`); // Required
}
}
}
Expand Down Expand Up @@ -305,7 +307,7 @@ As of Parse Server 3.7.0 you can use [PhantAuth](https://www.phantauth.net/).
{
"phantauth": {
"id": "user's PhantAuth sub (string)",
"access_token": "an authorized PhantAuth access token for the user",
"access_token": "an authorized PhantAuth access token for the user"
}
}
```
Expand Down Expand Up @@ -405,3 +407,168 @@ For more information about custom auth please see the examples:
- [Twitter OAuth](https://github.com/parse-community/parse-server/blob/master/src/Adapters/Auth/twitter.js)
- [Instagram OAuth](https://github.com/parse-community/parse-server/blob/master/src/Adapters/Auth/instagram.js)
- [Microsoft Graph OAuth](https://github.com/parse-community/parse-server/blob/master/src/Adapters/Auth/microsoft.js)

## Multi-factor authentication

### Time-based One-Time passwords (TOTP)

Time-based one-time passwords are considered best practise for multi-factor authentication, as SMS OTPs can be intercepted via [SS7](https://www.theguardian.com/technology/2016/apr/19/ss7-hack-explained-mobile-phone-vulnerability-snooping-texts-calls) or [sim swap](https://us.norton.com/blog/mobile/sim-swap-fraud) attacks. TOTP authentication can be configured using:

```js
{
auth: {
mfa: {
enabled: true,
options: ['TOTP'],
algorithm: 'SHA1',
digits: 6,
period: 30,
},
},
}
```

To enable MFA for a user, the [OTPAuth](https://github.com/hectorm/otpauth) package can be used.

First, create an TOTP object:

```js
const secret = new OTPAuth.Secret();
const totp = new OTPAuth.TOTP({
algorithm: "SHA1",
digits: 6,
period: 30,
secret,
});
```

Next, ask the user to add the TOTP code to their authenticator app:

```js
const uri = totp.toString();
```

This URI can also be scanned as a QR code, using the [QRCode](https://www.npmjs.com/package/qrcode) package.

```js
QRCode.toCanvas(document.getElementById("canvas"), uri);
```

Now, to confirm the user has correctly added the TOTP to their authenticator app, ask them to provide a valid code.

```js
const token = ""; // user inputted code
await user.save({
authData: {
mfa: {
secret: secret.base32, // secret is from the TOTP object above
token, // token is generated from the users authenticator app
},
},
});
```

Now, MFA will be enabled for the user. You can access recovery keys by:

```js
const recovery = user.get("authDataResponse");
```

It's also recommended to clear the authData from the client side:
dblythy marked this conversation as resolved.
Show resolved Hide resolved

```js
await user.fetch();
```

Now, when this user logs in, the will need to provide a valid MFA code:

```js
const login = async () => {
try {
await Parse.User.logIn(username, password);
} catch (e) {
if (e.message === 'Missing additional authData mfa') {
// show code input dialog here
}
}
}
const loginWithTOTP = async () => {
try {
await Parse.User.logInWithAdditionalAuth(username, password, {
mfa: // mfa code here
});
} catch (e) {
// display error
}
}
```

### SMS One-Time passwords

It is recommended to use TOTP MFA over SMS OTPs as SMS OTPs can be intercepted via [SS7](https://www.theguardian.com/technology/2016/apr/19/ss7-hack-explained-mobile-phone-vulnerability-snooping-texts-calls) or [sim swap](https://us.norton.com/blog/mobile/sim-swap-fraud) attacks.
dblythy marked this conversation as resolved.
Show resolved Hide resolved

```js
{
auth: {
mfa: {
enabled: true,
options: ['SMS'],
sendSMS(otp, mobileNumber) {
// Use an SMS service to send the SMS OTP
},
digits: 6,
period: 30,
},
},
}
```

To enable SMS MFA for a user, first set the users' mobile number.

```js
await user.save({ authData: { mfa: { mobile: "+11111111111" } } });
dblythy marked this conversation as resolved.
Show resolved Hide resolved
```

Next, ask the user to confirm the SMS code they just received

```js
await user.save({ authData: { mfa: { mobile: "+11111111111", token: code } } });
```

Now, SMS MFA will be enabled for the user. You can access recovery keys by accessing:

```js
const recovery = user.get("authDataResponse");
```

It's also recommended to clear the authData from the client side:
mtrezza marked this conversation as resolved.
Show resolved Hide resolved

```js
await user.fetch();
```

Now, when this user logs in, the will need to provide a valid MFA code:

```js
const login = async () => {
try {
await Parse.User.logIn(username, password);
} catch (e) {
if (e.message === 'Missing additional authData mfa') {
// show code input dialog here
await Parse.User.logInWithAdditionalAuth(username, password, {
mfa: true // this triggers an SMS to be sent
});
}
}
}
const loginWithTOTP = async () => {
try {
await Parse.User.logInWithAdditionalAuth(username, password, {
mfa: // mfa code here
});
} catch (e) {
// display error
}
}
```