Social Authentication
What you'll learn
- Log into your application via Google, Facebook, or Microsoft through the UI
with
cy.origin()using Auth0 social connections. The same patterns will work with Github, LinkedIn, and others. - Understanding challenges with social connections
Cypress does not recommend testing social connection authentication as a primary means of application authentication testing. This is due to the challenges mentioned in our Best Practices Guide.
If testing user based authentication flows via social connections is a must for your application, we recommend testing this in open mode (or run mode locally and headed) to avoid A/B tests or alternate login flows. Relying on social authentication in CI will likely result in bot detection and in some cases, account suspension due to violating the provider's Terms of Service.
Application Setup
For this guide, we are going to set up an application that uses Auth0 for Tenant will have social connections set up for Google, Facebook, and Microsoft which will authenticate users on our behalf to our application.
Setting up the Auth0 tenant
- To get started, first create your free Auth0
account and select the
"I am a new Auth0 User"flow. This will have 4 steps:- Step 1: When configuring your Sample App, please select the
Single-Page Appfor you platform andReactfor the technology. - Step 2: For
Social Connections, please select Facebook, Microsoft, and Google - Step 3: Click try login and verify the connections are set up properly
- Step 4: You can either download the sample on the
Download Sample Appbutton or clone it here. We will need this application in the next step. Select"I am Ready to Use Auth0". Your social connections on your Auth0 tenant should now be configured correctly
- Step 1: When configuring your Sample App, please select the
Setting up the application
On your Auth0 dashboard, visit the Applications Dropdown in the left menu.
Select the dropdown and go to Applications
- Select the
Default Application - Visit the
Settingstab- Copy the
domainandclient IDfields insideBasic Information. These will be needed shortly. - The
Application Propertiesshould haveSingle Page Applicationselected forApplication Type, - Inside
Application URIs, please addhttp://localhost:3000toAllowed Callback URLs,Allowed Web OriginsandAllowed Logout URLs
- Copy the
- Create the
src/auth_config.jsonfile from thesrc/auth_config.json.examplefile as described in the sample app. - Once created, paste the
domainandclient IDinto their respective fields insidesrc/auth_config.json. Theaudiencekey can be removed. The login/callback URIs will default tohttp://localhost:3000which is the url of our test application. - install the application dependencies and start the dev server
- Verify the application is running correctly by visiting
http://localhost:3000and clicking the blueLog inbutton in the top right corner. Select one of our social connections and log in and make sure to complete theAuthorize Appprompt. This only needs to be done once. This should take you back to the sample app authenticated as the test user. When finished, click theLog Outdropdown button. Repeat for Google, Microsoft, and Facebook or the accounts you would like to test.
Here is what this process should look like for Facebook setup:
Testing with Cypress
Now that our application is set up and ready to go, we are ready to write our
automated tests. For these social connections, the
experimentalModifyObstructiveThirdPartyCode
configuration option must be enabled.
Setting Auth0 app credentials in Cypress
To have access to test user credentials within our tests we need to configure
Cypress to use the social username, password, and name environment variables set
in the cypress.env.json file or by one of our
supported methods.
// cypress.env.json
{
"GOOGLE_USERNAME": "",
"GOOGLE_PASSWORD": "",
"GOOGLE_NAME": "",
"MICROSOFT_USERNAME": "",
"MICROSOFT_PASSWORD": "",
"MICROSOFT_NAME": "",
"FACEBOOK_USERNAME": "",
"FACEBOOK_PASSWORD": "",
"FACEBOOK_NAME": ""
}
Login with cy.origin()
Next, we'll write a custom command called loginToAuth0ViaSocial to perform a
login to either Facebook, Google, or Microsoft. This command will use
cy.origin() to:
- Navigate to the Auth0 login
- Select the
Continue with ...button and sign in with social credentials - Sign in and redirect back to the sample app
- Cache the results with
cy.session()
For demonstration, we will have a function for each provider.
The Facebook Login Function
// cypress/support/commands.ts
import { domain as Auth0Domain } from '../../src/auth_config.json'
function logIntoFacebook(username: string, password: string, name: string) {
cy.visit('http://localhost:3000')
cy.get('#qsLoginBtn').click()
cy.origin(Auth0Domain, () => {
cy.scrollTo('bottom')
cy.get('form[data-provider="facebook"]').submit()
})
cy.origin(
'https://www.facebook.com',
{
args: {
username,
password,
},
},
({ username, password }) => {
cy.get('input#email').type(username)
cy.get('input#pass').type(password, {
log: false,
})
cy.get('[type="submit"]').contains('Log In').click()
}
)
cy.get('h6.dropdown-header').should('contain', name)
}
The Google Login Function
// cypress/support/commands.ts
import { domain as Auth0Domain } from '../../src/auth_config.json'
function logIntoGoogle(username: string, password: string, name: string) {
Cypress.on(
'uncaught:exception',
(err) =>
!err.message.includes('ResizeObserver loop') &&
!err.message.includes('Error in protected function')
)
cy.visit('http://localhost:3000')
cy.get('#qsLoginBtn').click()
cy.origin(Auth0Domain, () => {
cy.scrollTo('bottom')
cy.get('form[data-provider="google"]').submit()
})
cy.origin(
'https://accounts.google.com',
{
args: {
username,
password,
},
},
({ username, password }) => {
Cypress.on(
'uncaught:exception',
(err) =>
!err.message.includes('ResizeObserver loop') &&
!err.message.includes('Error in protected function')
)
cy.get('input[type="email"]').type(username, {
log: false,
})
// NOTE: The element exists on the original form but is hidden and gets rerendered, which leads to intermittent detached DOM issues
cy.contains('Next').click().wait(4000)
cy.get('[type="password"]').type(password, {
log: false,
})
cy.contains('Next').click().wait(4000)
}
)
cy.get('h6.dropdown-header').should('contain', name)
}
And lastly, our Microsoft Login Function
// cypress/support/commands.ts
import { domain as Auth0Domain } from '../../src/auth_config.json'
function logIntoMicrosoft(username: string, password: string, name: string) {
cy.visit('http://localhost:3000')
cy.get('#qsLoginBtn').click()
cy.origin(Auth0Domain, () => {
cy.scrollTo('bottom')
cy.get('form[data-provider="windowslive"]').submit()
})
cy.origin(
'login.live.com',
{
args: {
username,
password,
},
},
({ username, password }) => {
cy.get('input[type="email"]').type(username)
cy.get('input[type="submit"]').click()
cy.get('input[type="password"]').type(password, {
log: false,
})
cy.get('input[type="submit"]').click()
cy.get('#idBtn_Back').click()
}
)
cy.get('h6.dropdown-header').should('contain', name)
}
We can wire these functions together in a single command, like so:
// cypress/support/commands.ts
Cypress.Commands.add(
'loginToAuth0ViaSocial',
(SOCIAL_PROVIDER: 'microsoft' | 'google' | 'facebook') => {
const log = Cypress.log({
displayName: 'Social LOGIN',
message: [`🔐 Authenticating | ${SOCIAL_PROVIDER}`],
// @ts-ignore
autoEnd: false,
})
log.snapshot('before')
switch (SOCIAL_PROVIDER) {
case 'microsoft':
logIntoMicrosoft(
Cypress.env('MICROSOFT_USERNAME'),
Cypress.env('MICROSOFT_PASSWORD'),
Cypress.env('MICROSOFT_NAME')
)
break
case 'google':
logIntoGoogle(
Cypress.env('GOOGLE_USERNAME'),
Cypress.env('GOOGLE_PASSWORD'),
Cypress.env('GOOGLE_NAME')
)
break
case 'facebook':
logIntoFacebook(
Cypress.env('FACEBOOK_USERNAME'),
Cypress.env('FACEBOOK_PASSWORD'),
Cypress.env('FACEBOOK_NAME')
)
break
default:
throw new Error('no social provider configured!')
}
log.snapshot('after')
log.end()
}
)
Now, we can use our loginToAuth0ViaSocial command in the test. Below is our
test to login as a user via Auth0 and run a basic check.
describe('Social Logins Demo', () => {
beforeEach(() => {
// can provide Facebook, Google, or Microsoft here
cy.loginToAuth0ViaSocial('facebook')
cy.visit('http://localhost:3000/')
})
it('navigates to the app after navigation and displays the sample project header', () => {
cy.get('h1').should('contain', 'React.js Sample Project')
})
})
Here is a sample of what all 3 should look like in order.
Lastly, we can refactor our login command to take advantage of
cy.session() to store our logged in user so we don't
have to reauthenticate before every test. This also largely cuts down on the
likelihood your account is blocked for frequent authentication attempts. Feel
free to DRY this up a bit!
Cypress.Commands.add(
'loginToAuth0ViaSocial',
(SOCIAL_PROVIDER: 'microsoft' | 'google' | 'facebook') => {
const log = Cypress.log({
displayName: 'Social LOGIN',
message: [`🔐 Authenticating | ${SOCIAL_PROVIDER}`],
// @ts-ignore
autoEnd: false,
})
log.snapshot('before')
cy.session(
`social-${SOCIAL_PROVIDER}`,
() => {
switch (SOCIAL_PROVIDER) {
case 'microsoft':
logIntoMicrosoft(
Cypress.env('MICROSOFT_USERNAME'),
Cypress.env('MICROSOFT_PASSWORD'),
Cypress.env('MICROSOFT_NAME')
)
break
case 'google':
logIntoGoogle(
Cypress.env('GOOGLE_USERNAME'),
Cypress.env('GOOGLE_PASSWORD'),
Cypress.env('GOOGLE_NAME')
)
break
case 'facebook':
logIntoFacebook(
Cypress.env('FACEBOOK_USERNAME'),
Cypress.env('FACEBOOK_PASSWORD'),
Cypress.env('FACEBOOK_NAME')
)
break
default:
throw new Error('no social provider configured!')
}
},
{
validate: () => {
cy.visit('http://localhost:3000')
switch (SOCIAL_PROVIDER) {
case 'microsoft':
cy.get('h6.dropdown-header').should(
'contain',
Cypress.env('MICROSOFT_NAME')
)
break
case 'google':
cy.get('h6.dropdown-header').should(
'contain',
Cypress.env('GOOGLE_NAME')
)
break
case 'facebook':
cy.get('h6.dropdown-header').should(
'contain',
Cypress.env('FACEBOOK_NAME')
)
break
default:
throw new Error('no social provider configured!')
}
},
}
)
log.snapshot('after')
log.end()
}
)
With the use of cy.session(), our tests should now
run quicker!
We hope this guide was able to get you up and running with
cy.origin() and
cy.session(). If you ran into any issues while
following this guide, or have any feedback, please let us know and submit a
Github issue.
Happy testing!