I have two apps: one is a Single Page Application (SPA) for my email/password login users, and the other is a Machine-to-Machine (M2M) app. Both apps are used on my website. I have also integrated a custom Single Sign-On (SSO) for users coming from SolidEarth IDP.
I’ve completed the custom SSO setup in my next js API route, as you can see in the code, and am able to retrieve data for users coming through the SSO. Now, I need to create a session for these users using my M2M app. Here’s what I am doing:
- Getting the client token via
/oauth/token
- Checking if the user exists via the email API
/api/v2/users-by-email
- Creating a user if not exists via
/api/v2/users
, or updating if the user exists via/api/v2/users/${id}
- Getting an Auth0 client token via
/oauth/token
and creating a session using that token
The session creation works fine on the API route, but when I redirect the user on the client side, there is no session, and Auth0 automatically redirects the user to the login page.
I want to know if it’s possible to manually create a session that will also be compatible with users coming through the Auth0 Universal Login page (classic).
I don’t want to use the enterprise SSO that Auth0 provides, as it is not free. I need a free alternative.
Is there any documentation or approach for handling this?
Any kind of help will be appreciated.
Thanks,
Here is my code, if it helps –
import { TokenSet } from "openid-client";
import { NextApiRequest, NextApiResponse } from "next";
import { v4 } from "uuid";
import {
auth0userExist,
createAuth0User,
getAuth0ClientToken,
getAuth0UserToken,
updateAuth0User,
} from "../../../src/shared/auth/actions";
import {
Claims,
getSession,
Session,
SessionCache,
updateSession,
} from "@auth0/nextjs-auth0";
import axios from "axios";
import { IncomingMessage, ServerResponse } from "http";
import {
NodeCookies,
StatefulSession,
StatelessSession,
} from "../../../src/auth0-session";
import { createApolloClient } from "../../../src/shared/apollo/client";
import { User } from "@auth0/auth0-react";
import * as gql from "../../../src/shared/auth/auth-queries";
import auth0 from "../../../utils/auth0";
import { getConfig } from "../../../src/config";
// import { handle8baseUser } from "../auth/callback";
/**
* Route to login.
*
* @param req - Request from next ctx.
* @param res - Response from next ctx.
*/
export default async function login(
req: NextApiRequest,
res: NextApiResponse
): Promise<void> {
let responseOidc: {
family_name: string;
OfficeName: string;
given_name: string;
email: string;
};
const { baseConfig } = getConfig();
const sessionStore = baseConfig.session.store
? new StatefulSession<
IncomingMessage | NextApiRequest,
ServerResponse | NextApiResponse,
Session
>(baseConfig, NodeCookies)
: new StatelessSession<
IncomingMessage | NextApiRequest,
ServerResponse | NextApiResponse,
Session
>(baseConfig, NodeCookies);
const sessionCache = new SessionCache(baseConfig, sessionStore as any);
try {
const result = await axios.get(
"https://miamirealtors.mysolidearth.com/oauth/userinfo",
{
headers: {
Authorization: "Bearer " + req.query.token,
},
}
);
responseOidc = result?.data;
} catch (error: any) {
res.status(400).json(error?.response?.data);
return;
}
const { email, OfficeName, family_name, given_name } = responseOidc;
const sessionR = await auth0.getSession(req, res);
// const sessionR = await getSession(req, res);
console.log("sessionR :>> ", sessionR);
try {
const password = v4();
const clientToken = await getAuth0ClientToken();
if (!clientToken)
return res.status(400).send({ message: "Failed to get client token" });
let userExist = await auth0userExist(clientToken, email);
if (!userExist) {
userExist = await createAuth0User(clientToken, email, password, {
user_metadata: {
name: `${given_name} ${family_name}`,
companyName: OfficeName,
},
});
} else {
userExist = await updateAuth0User(clientToken, userExist.user_id, {
password,
user_metadata: {
name: `${given_name} ${family_name}`,
companyName: OfficeName,
},
});
}
const userToken = await getAuth0UserToken(email, password);
console.log("auth0 userExist :>> ", userExist, userToken);
const session = await sessionCache.fromTokenSet(new TokenSet(userToken));
await sessionCache.create(req, res, session);
console.log(
"sessionCache. getIdToken:>> ",
await sessionCache.getIdToken(req, res)
);
console.log("sessionCache. get:>> ", await sessionCache.get(req, res));
console.log(
"sessionCache. isAuthenticated:>> ",
await sessionCache.isAuthenticated(req, res)
);
const updatedSession = {
...session,
user: userExist as Claims,
accessToken: userToken?.access_token || userToken?.accessToken,
idToken: userToken?.id_token || userToken?.idToken,
accessTokenExpiresAt: userToken.expires_in,
accessTokenScope: userToken.scope,
};
await auth0.updateSession(req, res, updatedSession);
// await updateSession(req, res, updatedSession);
console.log("session :>> ", session);
console.log("sessionR :>> ", sessionR);
// await handle8baseUser(updatedSession, true);
const client = createApolloClient(userToken?.id_token);
console.log("createApolloClient :>> ", client);
let metadata: User = {};
Object.keys(session.user).forEach((key) => {
if (key.includes("user_metadata")) metadata = session.user[key] || {};
});
/**
* Check if user exists in 8base.
*/
let response;
let exist = false;
try {
response = await client.query({
query: gql.CURRENT_USER_QUERY,
});
console.log("CURRENT_USER_QUERY response :>> ", response);
exist = true;
} catch {
/**
* If user doesn't exist, an error will be
* thrown, which then the new user can be
* created using the authResult values.
*/
console.log("creatingUser");
try {
response = await client.mutate({
mutation: gql.USER_SIGN_UP_MUTATION,
variables: {
user: {
email: session.user.email,
name: `${given_name} ${family_name}`,
companyName: OfficeName,
isFromRealtors: true,
},
authProfileId: process.env.AUTH_PROFILE_ID,
},
});
} catch (error) {
console.log("error", JSON.stringify(error));
throw error;
}
}
if (exist) {
await client.mutate({
mutation: gql.USER_UPDATE_MUTATION,
variables: {
data: {
id: response.data.user.id,
isFromRealtors: true,
},
},
});
}
res
.writeHead(302, {
Location: "/",
})
.end();
} catch (error: any) {
console.error(error);
res
.status(error?.status || 500)
.end(JSON.stringify({ error: error.message }));
}
}