Today, we will look at setting up authentication in your Qwik City apps with Auth.js. Qwik makes it easy to integrate Auth.js into your app by running the following command:
npm run qwik add auth
This will automatically create a 'plugin@auth.ts' file in the routes directory. You can read the official docs here: https://qwik.builder.io/docs/integrations/authjs/.
Inside the plugin@auth.ts we can pass a provider, for our example we will use google. You can see a list of providers here: https://next-auth.js.org/providers/.
import { serverAuth$ } from "@builder.io/qwik-auth";
import Google from "@auth/core/providers/google";
import type { Provider } from "@auth/core/providers";
import { api } from "../api";
export const { onRequest, useAuthSession, useAuthSignin, useAuthSignout } =
serverAuth$((req) => ({
secret: req.env.get("AUTH_SECRET"),
trustHost: true,
providers: [
Google({
clientId: req.env.get("GOOGLE_CLIENT_ID") as string,
clientSecret: req.env.get("GOOGLE_CLIENT_SECRET") as string,
authorization: {
params: {
prompt: "consent",
access_type: "offline",
response_type: "code",
},
},
}),
] as Provider[],
}));
This 'profile' function is called after a successful authentication and receives the user's profile data and tokens from the provider. You can store the users data in this function for future use.
import { serverAuth$ } from "@builder.io/qwik-auth";
import Google from "@auth/core/providers/google";
import type { Provider } from "@auth/core/providers";
import { api } from "../api";
export const { onRequest, useAuthSession, useAuthSignin, useAuthSignout } =
serverAuth$((req) => ({
secret: req.env.get("AUTH_SECRET"),
trustHost: true,
providers: [
Google({
clientId: req.env.get("GOOGLE_CLIENT_ID") as string,
clientSecret: req.env.get("GOOGLE_CLIENT_SECRET") as string,
authorization: {
params: {
prompt: "consent",
access_type: "offline",
response_type: "code",
},
},
profile: async (profile, tokens) => {
console.log(profile);
console.log(tokens);
//store profile data and/or access_token/refresh_token in the database here
return {
id: profile.sub,
name: profile.name,
email: profile.email,
image: profile.picture,
tokens,
};
},
}),
] as Provider[],
}));
You can use 'useAuthSession' to get the users session data but sometimes you may want to access it in the requestEvent. You can do so with 'sharedMap'.
export const useUserSession = routeLoader$(async (requestEvent) => {
const appointments = await api(requestEvent).appointments.query.getByEmail({
email: requestEvent.sharedMap.get("session")?.user?.email,
});
console.log(appointments);
return appointments;
});
You can also access the session cookie and decode the JWT.
import { decode } from "@auth/core/jwt";
export const onRequest: RequestHandler = async ({ cookie, env }) => {
const sessionToken = cookie.get("next-auth.session-token");
console.log(sessionToken?.value);
const decoded = await decode({
token: sessionToken?.value,
secret: env.get("AUTH_SECRET") as string,
});
console.log(decoded?.email);
};
Protected Routes
export const onRequest: RequestHandler = (event) => {
const session: Session | null = event.sharedMap.get('session');
if (!session || new Date(session.expires) < new Date()) {
throw event.redirect(302, `/api/auth/signin?callbackUrl=${event.url.pathname}`);
}
};