Parse Server supports 3rd party authentication with
- Apple
- Apple Game Center
- Github
- Google Play Game Services
- Janrain Capture
- Janrain Engage
- Keycloak
- LDAP
- Line
- Meetup
- Microsoft Graph
- OAuth
- PhantAuth
- Spotify
- vKontakte
Configuration options for these 3rd-party modules is done with the auth
option passed to Parse Server:
{
auth: {
twitter: {
consumer_key: "", // REQUIRED
consumer_secret: "" // REQUIRED
},
facebook: {
appIds: "FACEBOOK APP ID"
}
}
}
Below, you will find all expected payloads for logging in with a 3rd party auth.
Note, most of them don't require a server configuration so you can use them directly, without particular server configuration.
The options passed to Parse Server:
{
auth: {
facebook: {
appIds: ['appId1', 'appId2'], // If set, the app ID is used to validate the authentication token provided by the client when authenticating.
},
}
}
Learn more about Facebook login.
{
"twitter": {
"id": "user's Twitter id number as a string",
"consumer_key": "your application's consumer key",
"consumer_secret": "your application's consumer secret",
"auth_token": "an authorized Twitter token for the user with your application",
"auth_token_secret": "the secret associated with the auth_token"
}
}
The options passed to Parse Server:
{
auth: {
twitter: {
consumer_key: "", // REQUIRED
consumer_secret: "" // REQUIRED
},
}
}
Learn more about Twitter login.
{
"anonymous": {
"id": "random UUID with lowercase hexadecimal digits"
}
}
As of Parse Server 3.5.0 you can use Sign In With Apple.
{
"apple": {
"id": "user",
"token": "the identity token for the user"
}
}
Using Apple Sign In on a iOS device will give you a ASAuthorizationAppleIDCredential.user
string for the user identifier, which can be match the sub
component of the JWT identity token.
Using Apple Sign In through the Apple JS SDK or through the REST service will only give you the JWT identity token (id_token
) which you'll have to decompose to obtain the user identifier in its sub
component. As an example you could use something like JSON.parse(atob(token.split(".")[1])).sub
.
{
auth: {
apple: {
clientId: 'com.example.app', // optional, for extra validation; replace with the bundle ID provided by Apple.
},
}
}
Learn more about Sign In With Apple.
{
"github": {
"id": "user's Github id (string)",
"access_token": "an authorized Github access token for the user"
}
}
Google oauth supports validation of id_token's and access_token's.
{
"google": {
"id": "user's Google id (string)",
"id_token": "an authorized Google id_token for the user (use when not using access_token)",
"access_token": "an authorized Google access_token for the user (use when not using id_token)"
}
}
{
"instagram": {
"id": "user's Instagram id (string)",
"access_token": "an authorized Instagram access token for the user",
"apiURL": "an api url to make requests. Default: https://api.instagram.com/v1/"
}
}
{
"keycloak": {
"access_token": "access token from keycloak JS client authentication",
"id": "the id retrieved from client authentication in Keycloak",
"roles": ["the roles retrieved from client authentication in Keycloak"],
"groups": ["the groups retrieved from client authentication in Keycloak"]
}
}
The authentication module will test if the authData is the same as the userinfo oauth call, by comparing the attributes.
Copy the JSON config file generated on Keycloak (tutorial)
and paste it inside of a folder (Ex.: auth/keycloak.json
) in your server.
The options passed to Parse Server:
{
auth: {
keycloak: {
config: require(`./auth/keycloak.json`); // Required
}
}
}
The LDAP module can check if a
user can authenticate (bind) with the given credentials. Optionally, it can also check if the user is in a certain group.
This check is done using a user specified query, called an LDAP Filter.
The query should return all groups which the user is a member of. The cn
attribute of the query results is compared to groupCn
.
To build a query which works with your LDAP server, you can use a LDAP client like Apache Directory Studio.
{
"ldap": {
"url": "ldap://host:port",
"suffix": "the root of your LDAP tree",
"dn": "Bind dn. {{id}} is replaced with the id suppied in authData",
"groupCn": "Optional. A group which the user must be a member of.",
"groupFilter": "Optional. An LDAP filter for finding groups which the user is part of. {{id}} is replaced with the id supplied in authData."
}
}
If either groupCN
or groupFilter
is not specified, the group check is not performed.
Example Configuration (this works with the public LDAP test server hosted by Forumsys):
{
"ldap": {
"url": "ldap://ldap.forumsys.com:389",
"suffix": "dc=example,dc=com",
"dn": "uid={{id}}, dc=example, dc=com",
"groupCn": "Chemists",
"groupFilter": "(&(uniqueMember=uid={{id}},dc=example,dc=com)(objectClass=groupOfUniqueNames))"
}
}
authData:
{
"authData": {
"ldap": {
"id": "user id",
"password": "password"
}
}
}
{
"linkedin": {
"id": "user's LinkedIn id (string)",
"access_token": "an authorized LinkedIn access token for the user",
"is_mobile_sdk": true|false // set to true if you acquired the token through LinkedIn mobile SDK
}
}
{
"meetup": {
"id": "user's Meetup id (string)",
"access_token": "an authorized Meetup access token for the user"
}
}
{
"microsoft": {
"id": "user's microsoft id (string)", // required
"access_token": "an authorized microsoft graph access token for the user", // required
"mail": "user's microsoft email (string)"
}
}
Learn more about Microsoft Graph Auth Overview.
To get access on behalf of a user.
As of Parse Server 3.7.0 you can use PhantAuth.
{
"phantauth": {
"id": "user's PhantAuth sub (string)",
"access_token": "an authorized PhantAuth access token for the user"
}
}
Learn more about PhantAuth.
{
"qq": {
"id": "user's QQ id (string)",
"access_token": "an authorized QQ access token for the user"
}
}
{
"spotify": {
"id": "user's spotify id (string)",
"access_token": "an authorized spotify access token for the user"
}
}
{
"vkontakte": {
"id": "user's vkontakte id (string)",
"access_token": "an authorized vkontakte access token for the user"
}
}
{
auth: {
vkontakte: {
appSecret: "", // REQUIRED, your vkontakte application secret
appIds: "" // REQUIRED, your vkontakte application id
},
}
}
{
"wechat": {
"id": "user's wechat id (string)",
"access_token": "an authorized wechat access token for the user"
}
}
{
"weibo": {
"id": "user's weibo id (string)",
"access_token": "an authorized weibo access token for the user"
}
}
It is possible to leverage the OAuth support with any 3rd party authentication that you bring in.
{
auth: {
my_custom_auth: {
module: "PATH_TO_MODULE" // OR object,
option1: "",
option2: "",
}
}
}
On this module, you need to implement and export those two functions validateAuthData(authData, options) {}
and validateAppId(appIds, authData, options) {}
.
For more information about custom auth please see the examples:
Time-based one-time passwords are considered best practise for multi-factor authentication, as SMS OTPs can be intercepted via SS7 or sim swap attacks. TOTP authentication can be configured using:
{
auth: {
mfa: {
enabled: true,
options: ['TOTP'],
algorithm: 'SHA1',
digits: 6,
period: 30,
},
},
}
To enable MFA for a user, the OTPAuth package can be used.
First, create an TOTP object:
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:
const uri = totp.toString();
This URI can also be scanned as a QR code, using the QRCode package.
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.
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:
const recovery = user.get("authDataResponse");
It's also recommended to clear the authData from the client side:
await user.fetch();
Now, when this user logs in, the will need to provide a valid MFA code:
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
}
}
It is recommended to use TOTP MFA over SMS OTPs as SMS OTPs can be intercepted via SS7 or sim swap attacks.
{
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.
await user.save({ authData: { mfa: { mobile: "+11111111111" } } });
Next, ask the user to confirm the SMS code they just received
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:
const recovery = user.get("authDataResponse");
It's also recommended to clear the authData from the client side:
await user.fetch();
Now, when this user logs in, the will need to provide a valid MFA code:
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
}
}