@@ -32,6 +32,67 @@ const token_endpoint_auth_method = OAUTH2_CLIENT_SECRET
3232 ? 'client_secret_basic'
3333 : 'none' ;
3434
35+ const calculateExpirationDate = ( expiresIn ?: number ) => {
36+ if ( ! expiresIn ) {
37+ return undefined ;
38+ }
39+
40+ return Math . floor ( Date . now ( ) / 1000 ) + expiresIn ;
41+ } ;
42+ const getWellKnownData = async ( ) => {
43+ const wellKnownUrl = new URL ( OIDC_CONF_FULL_WELL_KNOWN_URL ) ;
44+ const wellKnown = await fetch ( wellKnownUrl ) ;
45+ return await wellKnown . json ( ) ;
46+ } ;
47+ async function refreshAccessToken ( token : JWT ) : Promise < JWT > {
48+ const { token_endpoint } = await getWellKnownData ( ) ;
49+
50+ try {
51+ const raw = {
52+ client_id : OAUTH2_CLIENT_ID ,
53+ client_secret : OAUTH2_CLIENT_SECRET ,
54+ grant_type : 'refresh_token' ,
55+ refresh_token : token . refreshToken as string ,
56+ } ;
57+
58+ const response = await fetch ( token_endpoint , {
59+ method : 'POST' ,
60+ body : new URLSearchParams ( raw ) ,
61+ } ) ;
62+
63+ if ( response . ok ) {
64+ const data : {
65+ access_token : string ;
66+ expires_in : number ;
67+ refresh_token : string ;
68+ refresh_expires_in : number ;
69+ } = await response . json ( ) ;
70+
71+ return {
72+ ...token ,
73+ accessToken : data . access_token ,
74+ accessTokenExpiresAt : calculateExpirationDate ( data . expires_in ) ,
75+ refreshToken : data . refresh_token ,
76+ refreshTokenExpiresAt : calculateExpirationDate (
77+ data . refresh_expires_in ,
78+ ) ,
79+ } ;
80+ } else {
81+ console . error (
82+ 'An error occurred while refreshing the access token: ' ,
83+ response . statusText ,
84+ ) ;
85+ return token ;
86+ }
87+ } catch ( error ) {
88+ console . error (
89+ 'An error occurred while refreshing the access token: ' ,
90+ error ,
91+ ) ;
92+ return token ;
93+ }
94+ }
95+
3596const wfoProvider : OAuthConfig < WfoUserProfile > = {
3697 id : NEXTAUTH_PROVIDER_ID ,
3798 name : NEXTAUTH_PROVIDER_NAME ,
@@ -74,21 +135,46 @@ export const authOptions: AuthOptions = {
74135 providers : isOauth2Enabled ? [ wfoProvider ] : [ ] ,
75136 callbacks : {
76137 async jwt ( { token, account, profile } ) {
138+ // First time after signing in
77139 // The "account" is only available right after signing in -- adding useful data to the token
78140 if ( account ) {
79- token . accessToken = account . access_token ;
80- token . profile = profile ;
141+ return {
142+ ...token ,
143+ accessToken : account . access_token ,
144+ refreshToken : account . refresh_token ,
145+ accessTokenExpiresAt : account . expires_at as number ,
146+ refreshTokenExpiresAt : calculateExpirationDate (
147+ account . refresh_expires_in as number ,
148+ ) ,
149+ profile,
150+ } ;
81151 }
82- return token ;
152+
153+ const now = Math . floor ( Date . now ( ) / 1000 ) ;
154+ if (
155+ typeof token . accessTokenExpiresAt === 'number' &&
156+ now < token . accessTokenExpiresAt
157+ ) {
158+ return token ;
159+ }
160+
161+ return await refreshAccessToken ( token ) ;
83162 } ,
84- async session ( { session, token } : { session : WfoSession ; token : JWT } ) {
163+ async session ( {
164+ session,
165+ token,
166+ } : {
167+ session : WfoSession ;
168+ token : JWT ;
169+ } ) : Promise < WfoSession > {
85170 // Assign data to the session to be available in the client through the useSession hook
86- session . profile = token . profile as WfoUserProfile | undefined ;
87- session . accessToken = token . accessToken
88- ? String ( token . accessToken )
89- : '' ;
90-
91- return session ;
171+ return {
172+ ...session ,
173+ profile : token . profile as WfoUserProfile | undefined ,
174+ accessToken : token . accessToken ? String ( token . accessToken ) : '' ,
175+ accessTokenExpiresAt : token . accessTokenExpiresAt as number ,
176+ refreshTokenExpiresAt : token . refreshTokenExpiresAt as number ,
177+ } ;
92178 } ,
93179 } ,
94180} ;
0 commit comments