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 all 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
175 changes: 173 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,172 @@ 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, so that the recovery keys cannot be accessed by inspecing the console:

```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 are sent in plain-text and can be intercepted by attacks at the client end, such as sim-swap attacks, or SS7 interception attacks.

```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: "+15555555555" } } });
```

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

```js
await user.save({ authData: { mfa: { mobile: "+15555555555", 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, so that the recovery keys cannot be accessed by inspecing the console:

```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: {
token: 'request',
} // this triggers an SMS to be sent
});
}
}
}
const loginWithTOTP = async () => {
try {
await Parse.User.logInWithAdditionalAuth(username, password, {
mfa: {
token: // mfa code here
}
});
} catch (e) {
// display error
}
}
```