Using OAuth SSO with Velo

Single Sign On (SSO) is a great way to make life easier for your site visitors and encourage them to register as a user on your site. Visitors can log in to your site using their Google, Facebook, or other accounts, or using their work account.

This article will look at how to implement OAuth - Open Authentication protocol, which is used by many service providers including Google, Facebook and Twitter. To learn about implementing SSO with SAML, see Using SAML SSO with Velo.

OAuth Flow

An authenticated user session is a session in which the identity of the user has been verified to the server, in the case of OAuth, by a 3rd-party.

The diagram below shows the flows involved in establishing an authenticated user session on a Wix site using Google as an OAuth identity provider. 

The flows are as follows:

  1. The user clicks Sign in with google.
  2. The Wix site requests an authorization URL.
  3. The browser is forwarded to the OAuth provider using the authorization URL, and the user signs in.
  4. The OAuth provider calls the Wix site URL, passing it an authorization code.
  5. The Wix site backend requests an access token from the OAuth provider using the authorization code.
  6. The Wix site backend requests user information using the access token.
  7. The Wix site backend creates a Wix session token using the user's email address.
  8. The Wix site backend returns a redirect URL to the OAuth provider including the session token.
  9. The OAuth provider redirects the browser using the returned URL.
  10. The browser redirects to the Wix frontend page using the URL.
  11. The Wix site uses the session ID in the URL to log the user in.

Setting up Google

In the Google Cloud Platform, we're going to set up Credentials. Follow this guide to setting up OAuth 2.0 from Google. 

Copy down the Client ID, Client secret and Authorized Redirect URL. We're going to use them later in our code.

Using the Secrets Manager

We'll use the Wix Secrets Manager to securely store our client secret. This is more secure than pasting it to our backend code. Make sure never to expose your client secret.

In the Secrets Manager, store a new secret with the following values:

  • Name : clientSecret
  • Value: The client secret value you retrieved from the Google Cloud Platform dashboard in the previous section.
  • Description:  Something to help you remember what this secret is used for.
If you haven't used the Secrets Manager before, here's how to use it.
  1. Go to your Wix Dashboard.

  2. Select Developer Tools and then Secrets Manager.

  3. Click the Store Secret button and enter the following details:

  4. Name: The name of the secret to be used in the code.

  5. Value: Paste the text value of the secret.

  6. Enter a brief description to remind you what this secret is used for, and click Save.

Setting up Velo

Before we start coding we need to install the Google APIs package so we can call its functions from our code.

To install a package in the editor:

  1. In the left sidebar of the editor, navigate to Packages & Apps. In Wix Studio, you can find this in the Code tab.
  2. Under npm click Install packages from npm.
  3. Search for "googleapis" and click Install.

Adding the Code

We'll need two code files:

  • OAuth.web.js. Where we will create a connection to Google and generate an authorization URL.
  • http-functions.js. To handle the callback from Google and retrieve the user's details.

Note that you must use the file name http-functions.js to handle the web callbacks but you can call the other file any name you like.

Getting the Authorization URL When the Visitor Clicks "Sign in to Google"

Landing page code

Copy
1
import wixLocationFrontend from 'wix-location-frontend';
2
import {session} from 'wix-storage-frontend';
3
import { getAuthUrl } from 'backend/OAuth.web';
4
5
$w.onReady(function () {
6
// handle the click event on the Sign in with Google button
7
$w('#btnSignInToGoogle').onClick((event) => {
8
googleSignin()
9
});
10
})
11
12
export function googleSignin() {
13
getAuthUrl()
14
.then((result) => {
15
const authorizationUrl=result.authUrl
16
const state=result.state
17
// store the state variable for later use
18
session.setItem("requestState", state);
19
// direct the bowser to the authorization Url
20
wixLocationFrontend.to(authorizationUrl);
21
})
22
}

When the site visitor clicks Sign in to Google, our site calls the getAuthUrl function in OAuth.web.js at line 13. We create a connection to the OAuth service provider (Google in this case) and request an authorization URL.

The state variable is then stored for this session at line 18. It will be compared to the state variable that is returned with the session token to prevent anyone copying the returned URL and logging in with another browser.

Our frontend then calls wixLocationFrontend.to at line 20 to direct the browser to the authorization URL.

OAuth.web.js

First, we retrieve our client secret at line 8, and use our API credentials to connect to Google's OAuth servers at line 17.

Copy
1
import { Permissions, webMethod } from 'wix-web-module';
2
import { google } from 'googleapis';
3
import { getSecret } from 'wix-secrets-backend';
4
import crypto from 'crypto';
5
6
export const getAuthUrl = webMethod(Permissions.Anyone, async () => {
7
8
//retrieve the client secret from the Secret Manager
9
const googleClientSecret = await getSecret('clientSecret');
10
const googleConfig = {
11
clientId: '123456789123-12abc3def4g5hijk67lmnopqrest8u9v0.apps.googleusercontent.com',
12
clientSecret: googleClientSecret,
13
redirect: 'https://mywix987.wixsite.com/sso-example/_functions/getAuth',
14
};
15
16
// create a connection to google's authentication services
17
const authConnection = new google.auth.OAuth2(
18
googleConfig.clientId,
19
googleConfig.clientSecret,
20
googleConfig.redirect
21
);
22
23
const scope = [
24
'https://www.googleapis.com/auth/userinfo.email',
25
'https://www.googleapis.com/auth/userinfo.profile',
26
];
27
28
// generate a random state variable
29
const state = crypto.randomBytes(16).toString('hex');
30
31
// request an authorization code URL
32
const authUrl = authConnection.generateAuthUrl({
33
access_type: 'offline', // 'online' (default) or 'offline' (gets refresh_token)
34
prompt: 'consent',
35
scope: scope,
36
state: state,
37
});
38
39
return { state, authUrl };
40
});

Note the googleConfig object in OAuth.web.js.

Copy
1
const googleConfig = {
2
clientId: '123456789123-12abc3def4g5hijk67lmnopqrest8u9v0.apps.googleusercontent.com',
3
clientSecret: googleClientSecret,
4
redirect: 'https://mywix987.wixsite.com/sso-example/_functions/getAuth'
5
};

These are the values from the Credentials page that we set up earlier.
The getAuth function in the redirect is defined in http-functions.js

At line 29 we generate a random value for the state variable, which will be stored by the browser, and compared to the state variable that is returned with the session token. This will prevent anyone from copying the URL with the session token and using it to log in. 

At line 32, OAuth.web.js generates the authentication URL and returns it with the state variable, to our frontend. The frontend calls wixLocationFrontend.to using that URL to direct the browser to the OAuth provider - line 15 in the landing page code. At this point the visitor sees the Google sign in dialog and enters their user name and password. 

Once Google authenticates the visitor, it redirects back to our site to the URL defined in googleConfig.redirect in our code. We also set up that URL in the Google Cloud Platform console as the authorized redirect URI. From the sample code, this will redirect to the get_getAuth function which is defined in http-functions.js.

Handling the Redirect from Google

In get_getAuth, we retrieve the client secret from the Secrets Manager at line 11. Next, we take the request URL from Google and extract the authorization code, at line 19, and the state variable at line 20. This code is used to get the access token based on our clientId and clientSecret, at line 30. Once we have the access token we use it at line 34, to get the userInfo object from Google.

Using the email address from userInfo, at line 45 we generate a session token using the wix-members-backend function generateSessionToken. This token will allow us to log the user in to our Wix site. If the email address corresponds to an existing member, a session token for logging in that member is generated. If there is no existing member with the specified email address, a new member is created and a session token for logging in that member is generated.

http-functions.js.

Copy
1
import { response } from 'wix-http-functions';
2
import { google } from 'googleapis';
3
import { authentication } from 'wix-members-backend';
4
import { getSecret } from 'wix-secrets-backend';
5
import { fetch } from 'wix-fetch';
6
7
//google calls this function with a get, after the user signs in
8
export async function get_getAuth(request) {
9
10
// retrieve the client secret from the Secrets Manager
11
const googleClientSecret = await getSecret('clientSecret');
12
const googleConfig = {
13
clientId: '123456789123-12abc3def4g5hijk67lmnopqrest8u9v0.apps.googleusercontent.com',
14
clientSecret: googleClientSecret,
15
redirect: 'https://mywix987.wixsite.com/sso-example/_functions/getAuth'
16
};
17
18
//get the autorization code and state variable form the request URL
19
const code = await request.query.code
20
const state= await request.query.state
21
22
// create a connection to google's authentication services
23
const auth2 = new google.auth.OAuth2(
24
googleConfig.clientId,
25
googleConfig.clientSecret,
26
googleConfig.redirect
27
);
28
29
//get the access token from the request with the authorization code we got from google
30
const data = await auth2.getToken(code);
31
const tokens = data.tokens;
32
33
//get the user info using the access token
34
const userInfoRes = await fetch(`https://www.googleapis.com/oauth2/v2/userinfo?alt=json&access_token=${tokens.access_token}`, { "method": "get" })
35
36
if (!userInfoRes.ok) {
37
console.log("cound not get user info using access token")
38
}
39
40
//extract the user's email and profile picture URL
41
const userInfo = (await userInfoRes.json())
42
const userEmail = userInfo.email
43
const profilePicture = userInfo.picture
44
45
//now that we have the email we can use it to generate a Wix session token to use in the frontend
46
const sessionToken = await authentication.generateSessionToken(userEmail);
47
48
//return the url, session token, state variable, and profile picture to google to rediect the browser to our logged in page.
49
return response({
50
status: 302,
51
headers: { 'Location': `https://mywix987.wixsite.com/sso-example/loggedin?sessiontoken=${sessionToken}&responseState=${state}&profilepic=${profilePicture}` }
52
});
53
}

Back to Our Site Page

The last step in the http-functions.js process at line 49, is to return a 302 status to Google, with a redirect URL to our site's signed-in page. The URL includes the session token so that we can automatically log the user in and the state variable to protect against copying the URL. Google passes this back to the browser where our site page code uses the token to log the user in.

On our signed-in page, we take the URL and extract the query parameters, including the session token and the response state variable at lines 8-11. We then remove the query parameters from the URL to make it harder to copy.  At line 17 we retrieve the state variable that we saved before signing in.

On line 20, we compare the saved state variable to the state variable returned in the URL. If someone managed to intercept the returned URL, and paste it into a browser, they would not have the value of the saved state variable. The comparison would fail and they would not be signed in. If the saved state is the same as the returned state, then this is the same browser that made the sign in request, so we can sign the user in, using the wix-members-frontend applySessionToken function, at line 21 below.

Create a new page called signed-in. Make sure that your signed-in page has the correct URL. The URL should be https://mywix987.wixsite.com/sso-example/signed-in, replacing mywix987.wixsite.com to suit your site. Follow this guide to set the URL.

Copy
1
import wixLocationFrontend from 'wix-location-frontend';
2
import { authentication } from 'wix-members-frontend';
3
import {session} from 'wix-storage-frontend';
4
5
$w.onReady(function () {
6
7
// get the session token and the profile pic URL
8
const query = wixLocationFrontend.query;
9
const sessionToken = query.sessionToken;
10
const userPic = query.profilePic;
11
const responseState = query.responseState;
12
13
// remove the query parameters from the URL so they can't be copied.
14
wixLocationFrontend.queryParams.remove(['sessionToken', 'profilePic','responseState']);
15
16
// retrieve the state variable data we saved before signing in
17
const requestState=session.getItem("requestState")
18
19
// if the requestState matches the responseState, log the user in using the session token
20
if (sessionToken && (requestState === responseState)) {
21
authentication.applySessionToken(sessionToken).then(() => {
22
//set the profile picture on our landing page
23
$w('#image1').src=userPic;
24
})
25
}
26
})

Adapting the Code for Your Site

Make the following changes to customize the code to work on your site:

  • OAuth.web.js 

    Line 10 - googleConfig - client id and redirect must be changed for your site URL and Google credentials.

  • http-functions.js

    Line 12 - googleConfig - client id and redirect must be changed for your site URL and Google credentials.

    Line 51 - The Location URL must be changed to suit your site.

Was this helpful?
Yes
No