Skip to content
This repository has been archived by the owner on Aug 29, 2023. It is now read-only.

Added initial auth with zitadel #1

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
NEXTAUTH_URL=http://localhost:3000
ZITADEL_ISSUER=https://helloworld-i5gnzl.zitadel.cloud
ZITADEL_CLIENT_ID=3ae62434b38216058473
ZITADEL_CLIENT_SECRET=eefaff5f5fb25fbb76e0afa5443977b248322da3
NEXTAUTH_SECRET=eefaff5f5fb25fbb76e0afa5443977b248322da3
93 changes: 93 additions & 0 deletions app/api/auth/[...nextauth].ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import NextAuth from 'next-auth';
import { JWT } from 'next-auth/jwt';
import ZitadelProvider from 'next-auth/providers/zitadel';
import { Issuer } from 'openid-client';
import {User} from "@/next-auth";
async function refreshAccessToken(token: JWT): Promise<JWT> {
try {
const issuer = await Issuer.discover(process.env.ZITADEL_ISSUER ?? '');
const client = new issuer.Client({
client_id: process.env.ZITADEL_CLIENT_ID || '',
token_endpoint_auth_method: 'none',
});

const { refresh_token, access_token, expires_at } = await client.refresh(token.refreshToken as string);

return {
...token,
accessToken: access_token,
expiresAt: (expires_at ?? 0) * 1000,
refreshToken: refresh_token, // Fall back to old refresh token
};
} catch (error) {
console.error('Error during refreshAccessToken', error);

return {
...token,
error: 'RefreshAccessTokenError',
};
}
}

export default NextAuth({
providers: [
ZitadelProvider({
issuer: process.env.ZITADEL_ISSUER,
clientId: process.env.ZITADEL_CLIENT_ID,
clientSecret: process.env.ZITADEL_CLIENT_SECRET,
authorization: {
params: {
scope: `openid email profile`,
},
},
async profile(_, { access_token }) {
const { userinfo_endpoint } = await (
await fetch(`${process.env.ZITADEL_ISSUER}/.well-known/openid-configuration`)
).json();

const profile = await (
await fetch(userinfo_endpoint, {
headers: {
Authorization: `Bearer ${access_token}`,
},
})
).json();

return {
id: profile.sub,
name: profile.given_name,
email: profile.email,
image: profile.picture,
}
},
}),
],
session: {
maxAge: 12 * 60 * 60, // 12 hours
},
pages: {
signIn: '/login',
},
callbacks: {
async jwt({ token, user, account }) {
if (account) {
token.accessToken = account.access_token;
token.expiresAt = (account.expires_at as number) * 1000;
}
if (user) {
token.user = user as User;
}

if (Date.now() > (token.expiresAt as number)) {
delete token.accessToken;
}

return refreshAccessToken(token);
},
async session({ session, token }) {
session.user = token.user;
session.accessToken = token.accessToken;
return session;
},
},
});
29 changes: 29 additions & 0 deletions next-auth.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import 'next-auth/jwt';


export type User = {
id: string;
firstname: string;
lastname: string;
email: string;
picture: string;
username: string;
avatarUrl?: string;
}

declare module 'next-auth' {
interface Session {
accessToken?: string;
user: User;
}
}

declare module 'next-auth/jwt' {
interface JWT {
accessToken?: string;
user?: User;
refreshToken?: string;
expiresAt?: number;
error?: string;
}
}
4 changes: 3 additions & 1 deletion next.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
/** @type {import('next').NextConfig} */
const nextConfig = {}
const nextConfig = {
reactStrictMode: true,
}

module.exports = nextConfig