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

Add setSession method #198

Closed
wants to merge 2 commits into from
Closed

Conversation

zia-ecologi
Copy link

@zia-ecologi zia-ecologi commented Dec 1, 2020

Description

This PR adds a setSession method to the SDK for setting the session with new tokens, following authentication which is external from the regular authorization code login flow this library uses.

This is useful for example if you need interoperability between @auth0/nextjs-auth0 and an existing legacy sign up endpoint which uses the password grant type for silent authentication following account creation.

If you want to expose a route which creates a new user and then silently authenticates them (eg: /pages/api/signup.js):

// Importing the `auth0` package to use the password grant type
import { AuthenticationClient } from 'auth0';
import auth0 from '../../utils/auth0';
import { createUserInUpstreamService } from '../../utils/signup';

const auth0AuthClient = new AuthenticationClient({
  domain: process.env.NEXT_PUBLIC_AUTH0_DOMAIN,
  clientId: process.env.NEXT_PUBLIC_AUTH0_CLIENT_ID,
  clientSecret: process.env.AUTH0_CLIENT_SECRET
});

export default async function signup(req, res) {
  try {
    // POST http://api.acme.com/signup will create
    // the user in the ACME database and in Auth0
    await createUserInUpstreamService(req.body);

    // Once the user is created successfully then silently authenticate
    // them using the password grant type
    const tokens = await auth0AuthClient.oauth.passwordGrant({
      username: req.body.username,
      password: req.body.password,
      scope: 'openid profile email offline_access'
    });

    // Set the session with the tokens from the password grant
    await auth0.setSession(req, res, tokens);

    res.status(201).end();
  } catch (error) {
    console.error(error);
    res.status(error.status || 500).end(error.message);
  }
}

NOTE: For the above example to work you will need to enable the password grant type on your Auth0 client application settings dashboard.

References

Auth0 community link - https://community.auth0.com/t/silent-auto-login-for-user-after-completing-authentication-api-backed-custom-signup-form/29790

Other Auth0 community link - https://community.auth0.com/t/custom-auth0-log-in-sign-up-flow/47685

Testing

To test this functionality set up an example project and run the setSession method, following the example given in the README and PR description.

Run npm test

Run the following to test the changes build correctly

npm run clean
npm run lint
npm run build
  • This change adds test coverage for new/changed/fixed functionality

Checklist

  • I have added documentation for new/changed functionality in this PR or in auth0.com/docs
  • All active GitHub checks for tests, formatting, and security are passing
  • The correct base branch is being used, if not master

@zia-ecologi zia-ecologi requested a review from a team as a code owner December 1, 2020 17:24
@zia-ecologi zia-ecologi changed the title Set session handler Set session method Dec 1, 2020
@zia-ecologi zia-ecologi changed the title Set session method setSession method Dec 1, 2020
@zia-ecologi zia-ecologi changed the title setSession method Add setSession method Dec 1, 2020
@zia-ecologi zia-ecologi closed this Dec 8, 2020
@zia-ecologi zia-ecologi reopened this Dec 8, 2020
@adamjmcgrath
Copy link
Contributor

Hi @zia-ecologi - thanks for raising this PR

This SDK is for use with the Universal Login Page, so if you want to do signup with this SDK you should redirect the user to the universal login page to do signup, eg, for the new universal login experience:

// pages/api/signup.js
import auth0 from '../../lib/auth0';

export default async function login(req, res) {
  try {
    await auth0.handleLogin(req, res, { authParams: { screen_hint: 'signup' } });
  } catch (error) {
    console.error(error);
    res.status(error.status || 500).end(error.message);
  }
}

See this FAQ https://community.auth0.com/t/how-do-i-redirect-users-directly-to-the-hosted-signup-page/42520 for more information about redirecting users to the signup prompt, including information for users of the classic login experience.

@zia-ecologi
Copy link
Author

zia-ecologi commented Jan 25, 2021

Hi @zia-ecologi - thanks for raising this PR

This SDK is for use with the Universal Login Page, so if you want to do signup with this SDK you should redirect the user to the universal login page to do signup, eg, for the new universal login experience:

// pages/api/signup.js
import auth0 from '../../lib/auth0';

export default async function login(req, res) {
  try {
    await auth0.handleLogin(req, res, { authParams: { screen_hint: 'signup' } });
  } catch (error) {
    console.error(error);
    res.status(error.status || 500).end(error.message);
  }
}

See this FAQ https://community.auth0.com/t/how-do-i-redirect-users-directly-to-the-hosted-signup-page/42520 for more information about redirecting users to the signup prompt, including information for users of the classic login experience.

@adamjmcgrath Hi Adam thanks for taking a look at this.

We are looking to migrate our sign up experience over to using the Universal Login Page eventually.

However our existing system has been using resource owner password grant type and there is a fairly complicated multi-step sign up UX.

I've refactored the login flow over to authorization code flow using NextJS Auth0.

I must say I have found the setSession method is very useful for edge cases where you need to override the token cache programmatically using your own custom implementation.

For example if you were writing a Cypress test suite and wanted to pre-can the test environment with a user session programmatically...

or if you need to support silent authentication in an existing server endpoint which previously used server based ROPC flow to enable silent authentication.

In that case the following code is very useful for reducing the amount of work and UX changes needed to migrate to @auth0/next-js.

import { AuthenticationClient } from "auth0";
import { TokenSet } from "openid-client";
import auth0 from "../../lib/auth0";

const auth0AuthClient = new AuthenticationClient({
  domain: process.env.NEXT_PUBLIC_AUTH0_DOMAIN,
  clientId: process.env.NEXT_PUBLIC_AUTH0_CLIENT_ID,
  clientSecret: process.env.AUTH0_CLIENT_SECRET,
});

export async function handleSilentLoginOnSignup(
  req,
  res,
  signUpFn: () => Promise<Response>
) {
  try {
    const apiResponse = await signUpFn();

    const body = await apiResponse.json();

    if (apiResponse.ok) {
      const tokens = await auth0AuthClient.oauth.passwordGrant({
        username: req.body.email || req.body.emailAddress,
        password: req.body.password,
        scope: "openid profile email offline_access",
      });

      await auth0.setSession(req, res, new TokenSet(tokens));
    }

    res.status(apiResponse.status).send(body);
  } catch (error) {
    console.error(error);
    res.status(error.status || 500).end(error.message);
  }
}

Is there any way you could consider supporting people being able to programmatically hydrate the Token Cache?

@adamjmcgrath
Copy link
Contributor

Hi @zia-ecologi - thanks for sharing that Cypress use case. I'm going to discuss it with my team.

Regardless of the outcome, we're working on getting the Beta to General availability, so would only accept PRs against the beta branch

@zia-ecologi
Copy link
Author

Hi @zia-ecologi - thanks for sharing that Cypress use case. I'm going to discuss it with my team.

Regardless of the outcome, we're working on getting the Beta to General availability, so would only accept PRs against the beta branch

Thanks @adamjmcgrath - I would be happy to raise another equivalent PR to add this functionality to the beta branch.

@rbmdotdev
Copy link

This is the exact workflow I need to be able to support for the exact same reason, has this functionality made it into the library in a different form yet?

@adamjmcgrath
Copy link
Contributor

@zia-ecologi - we've added support for the Cypress use case in v2 by adding a new testing utility to the library called generateSessionCookie - more info here #816

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants