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.
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:
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.
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:
Go to your Wix Dashboard.
Select Developer Tools and then Secrets Manager.
Click the Store Secret button and enter the following details:
Name: The name of the secret to be used in the code.
Value: Paste the text value of the secret.
Enter a brief description to remind you what this secret is used for, and click Save.
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:
Click on the CODE FILES button on the left sidebar
Select Packages (npm) and click Install a New Package.
Search for "googleapis" and click Install
We'll need two code files:
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.
Landing page code
Copy Code
import wixLocationFrontend from 'wix-location-frontend';import {session} from 'wix-storage-frontend';import { getAuthUrl } from 'backend/OAuth.jsw';$w.onReady(function () {// handle the click event on the Sign in with Google button$w('#btnSignInToGoogle').onClick((event) => {googleSignin()});})export function googleSignin() {getAuthUrl().then((result) => {const authorizationUrl=result.authUrlconst state=result.state// store the state variable for later usesession.setItem("requestState", state);// direct the bowser to the authorization UrlwixLocationFrontend.to(authorizationUrl);})}
When the site visitor clicks Sign in to Google, our site calls the getAuthUrl function in OAuth.jsw 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 front end then calls wixLocationFrontend.to at line 20 to direct the browser to the authorization URL.
OAuth.jsw
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 Code
import { google } from 'googleapis';import { getSecret } from 'wix-secrets-backend';import crypto from 'crypto'export async function getAuthUrl() {//retrieve the client secret from the Secret Managerconst googleClientSecret = await getSecret('clientSecret');const googleConfig = {clientId: '123456789123-12abc3def4g5hijk67lmnopqrest8u9v0.apps.googleusercontent.com',clientSecret: googleClientSecret,redirect: 'https://mywix987.wixsite.com/sso-example/_functions/getAuth'};// create a connection to google's authentication servicesconst authConnection = new google.auth.OAuth2(googleConfig.clientId,googleConfig.clientSecret,googleConfig.redirect);const scope = ['https://www.googleapis.com/auth/userinfo.email','https://www.googleapis.com/auth/userinfo.profile',];// generate a random state variableconst state= crypto.randomBytes(16).toString('hex')// request an authorization code URLconst authUrl = authConnection.generateAuthUrl({access_type: 'offline', // 'online' (default) or 'offline' (gets refresh_token)prompt: 'consent',scope: scope,state: state});return {state,authUrl};}
Note the googleConfig object in OAuth.jsw.
Copy Code
const googleConfig = {clientId: '123456789123-12abc3def4g5hijk67lmnopqrest8u9v0.apps.googleusercontent.com',clientSecret: googleClientSecret,redirect: 'https://mywix987.wixsite.com/sso-example/_functions/getAuth'};
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.jsw generates the authentication URL and returns it with the state variable, to our front end. The front end 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.
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 Code
import { response } from 'wix-http-functions';import { google } from 'googleapis';import { authentication } from 'wix-members-backend';import { getSecret } from 'wix-secrets-backend';import { fetch } from 'wix-fetch';//google calls this function with a get, after the user signs inexport async function get_getAuth(request) {// retrieve the client secret from the Secrets Managerconst googleClientSecret = await getSecret('clientSecret');const googleConfig = {clientId: '123456789123-12abc3def4g5hijk67lmnopqrest8u9v0.apps.googleusercontent.com',clientSecret: googleClientSecret,redirect: 'https://mywix987.wixsite.com/sso-example/_functions/getAuth'};//get the autorization code and state variable form the request URLconst code = await request.query.codeconst state= await request.query.state// create a connection to google's authentication servicesconst auth2 = new google.auth.OAuth2(googleConfig.clientId,googleConfig.clientSecret,googleConfig.redirect);//get the access token from the request with the authorization code we got from googleconst data = await auth2.getToken(code);const tokens = data.tokens;//get the user info using the access tokenconst userInfoRes = await fetch(`https://www.googleapis.com/oauth2/v2/userinfo?alt=json&access_token=${tokens.access_token}`, { "method": "get" })if (!userInfoRes.ok) {console.log("cound not get user info using access token")}//extract the user's email and profile picture URLconst userInfo = (await userInfoRes.json())const userEmail = userInfo.emailconst profilePicture = userInfo.picture//now that we have the email we can use it to generate a Wix session token to use in the front endconst sessionToken = await authentication.generateSessionToken(userEmail);//return the url, session token, state variable, and profile picture to google to rediect the browser to our logged in page.return response({status: 302,headers: { 'Location': `https://mywix987.wixsite.com/sso-example/loggedin?sessiontoken=${sessionToken}&responseState=${state}&profilepic=${profilePicture}` }});}
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 Code
import wixLocationFrontend from 'wix-location-frontend';import { authentication } from 'wix-members-frontend';import {session} from 'wix-storage-frontend';$w.onReady(function () {// get the session token and the profile pic URLconst query = wixLocationFrontend.query;const sessionToken = query.sessionToken;const userPic = query.profilePic;const responseState = query.responseState;// remove the query parameters from the URL so they can't be copied.wixLocationFrontend.queryParams.remove(['sessionToken', 'profilePic','responseState']);// retrieve the state variable data we saved before signing inconst requestState=session.getItem("requestState")// if the requestState matches the responseState, log the user in using the session tokenif (sessionToken && (requestState === responseState)) {authentication.applySessionToken(sessionToken).then(() => {//set the profile picture on our landing page$w('#image1').src=userPic;})}})
Make the following changes to customize the code to work on your site:
OAuth.jsw
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.