import NextAuth from 'next-auth' import KeycloakProvider from 'next-auth/providers/keycloak' const secretSessionKey = process.env.SECRET_SESSION_KEY || 'UItTuD1HcGXIj8ZfHUswhYdNd40Lc325R8VlxQPUoR0=' const clientId = process.env.KEY_CLOAK_CLIENT_ID || 'gwdg-fdoman-test' const clientSecret = process.env.KEY_CLOAK_CLIENT_SECRET || '' const issuer = process.env.KEY_CLOAK_ISSUER || 'https://keycloak.sso.gwdg.de/auth/realms/academiccloud' async function refreshAccessToken (token: any) { try { const url = issuer + '/protocol/openid-connect/token' const params = new URLSearchParams({ client_id: clientId, client_secret: clientSecret, grant_type: 'refresh_token', refresh_token: token.refreshToken }).toString() const response = await fetch(url, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, method: 'POST', body: params }) const refreshedTokens = await response.json() if (!response.ok) { throw refreshedTokens } console.log('### refreshToken', new Date().toISOString(), refreshedTokens, '### ### ###') return { ...token, accessToken: refreshedTokens.access_token, accessTokenExpires: Date.now() + refreshedTokens.expires_in * 1000, refreshToken: refreshedTokens.refresh_token ?? token.refreshToken // Fall back to old refresh token } } catch (error) { console.log('### RefreshAccessTokenError', error, '### ### ###') return { ...token, error: 'RefreshAccessTokenError' } } } export const authOptions = { // adapter: UnstorageAdapter(storage), session: { maxAge: 172800 // 48h }, debug: true, // Configure one or more authentication providers providers: [ // !!! Should be stored in .env file. KeycloakProvider({ clientId, clientSecret, issuer, // authorization: { params: { scope: 'openid profile email' } }, profile (profile) { console.log('### profile', profile, '### ### ###') return { id: profile.sub, name: profile.name ?? profile.preferred_username } } }) ], callbacks: { async signIn ({ user, account, profile, email, credentials }: any) { console.log('### signIn', user, account, profile, email, credentials, '### ### ###') return true }, async session ({ session, user, token }: any) { console.log('### session', session, user, token, '### ### ###') session.user = token.user session.accessToken = token.accessToken session.error = token.error return session }, async redirect ({ url, baseUrl }: any) { console.log('### redirect', url, baseUrl, '### ### ###') return Promise.resolve(url) }, async jwt ({ token, user, account, profile, isNewUser }: any) { console.log('### jwt', token, user, account, profile, isNewUser, '### ### ###') // Initial sign in if (account && user) { return { lastRefresh: Date.now(), accessToken: account.access_token, accessTokenExpires: account.expires_at, refreshToken: account.refresh_token, user } } if (token.lastRefresh + 60000 > Date.now()) { // return if last refresh is 10 seconds or younger return token } // Access token has expired, try to update it return refreshAccessToken(token) } }, secret: secretSessionKey } export default NextAuth(authOptions)