@@ -11,7 +11,7 @@ import { Micro } from 'effect';
1111import { exitIsFail , exitIsSuccess } from 'effect/Micro' ;
1212
1313import { authorizeµ } from './authorize.request.js' ;
14- import { createClientStore } from './client.store.utils.js' ;
14+ import { createClientStore , createError } from './client.store.utils.js' ;
1515import { createValuesµ , handleTokenResponseµ , validateValuesµ } from './exchange.utils.js' ;
1616import { GenericError } from './error.types.js' ;
1717import { oidcApi } from './oidc.api.js' ;
@@ -20,7 +20,7 @@ import { wellknownApi, wellknownSelector } from './wellknown.api.js';
2020import type { ActionTypes , RequestMiddleware } from '@forgerock/sdk-request-middleware' ;
2121import type { GetAuthorizationUrlOptions } from '@forgerock/sdk-types' ;
2222
23- import type { OidcConfig } from './config.types.js' ;
23+ import type { OauthTokens , OidcConfig } from './config.types.js' ;
2424import type { AuthorizeErrorResponse } from './authorize.request.types.js' ;
2525import type { TokenExchangeErrorResponse } from './exchange.types.js' ;
2626
@@ -39,7 +39,7 @@ export async function oidc<ActionType extends ActionTypes = ActionTypes>({
3939 storage ?: Partial < StorageConfig > ;
4040} ) {
4141 const log = loggerFn ( { level : logger ?. level || 'error' , custom : logger ?. custom } ) ;
42- const storageClient = createStorage ( {
42+ const storageClient = createStorage < OauthTokens > ( {
4343 type : 'localStorage' ,
4444 name : 'oidcTokens' ,
4545 ...storage ,
@@ -159,7 +159,12 @@ export async function oidc<ActionType extends ActionTypes = ActionTypes>({
159159 Micro . flatMap ( ( { data, error } ) => handleTokenResponseµ ( data , error ) ) ,
160160 Micro . flatMap ( ( data ) =>
161161 Micro . promise ( async ( ) => {
162- await storageClient . set ( data ) ;
162+ await storageClient . set ( {
163+ accessToken : data . access_token ,
164+ idToken : data . id_token ,
165+ refreshToken : data . refresh_token ,
166+ expiresAt : data . expires_in ,
167+ } ) ;
163168 return data ;
164169 } ) ,
165170 ) ,
@@ -180,5 +185,140 @@ export async function oidc<ActionType extends ActionTypes = ActionTypes>({
180185 }
181186 } ,
182187 } ,
188+ user : {
189+ info : async ( ) => {
190+ const state = store . getState ( ) ;
191+ const wellknown = wellknownSelector ( wellknownUrl , state ) ;
192+
193+ if ( ! wellknown ?. userinfo_endpoint ) {
194+ const err = {
195+ error : 'Wellknown missing userinfo endpoint' ,
196+ type : 'wellknown_error' ,
197+ } as AuthorizeErrorResponse ;
198+
199+ log . error ( err . error ) ;
200+
201+ return err ;
202+ }
203+
204+ const tokens = await storageClient . get ( ) ;
205+
206+ if ( ! tokens || ! ( 'accessToken' in tokens ) ) {
207+ const err = {
208+ error : 'No access token found' ,
209+ type : 'auth_error' ,
210+ } as AuthorizeErrorResponse ;
211+
212+ log . error ( err . error ) ;
213+
214+ return err ;
215+ }
216+
217+ return await store . dispatch (
218+ oidcApi . endpoints . userInfo . initiate ( {
219+ accessToken : tokens . accessToken ,
220+ endpoint : wellknown . userinfo_endpoint ,
221+ } ) ,
222+ ) ;
223+ } ,
224+ logout : async ( ) => {
225+ const state = store . getState ( ) ;
226+ const wellknown = wellknownSelector ( wellknownUrl , state ) ;
227+
228+ if ( ! wellknown ?. end_session_endpoint ) {
229+ const err = {
230+ error : 'Wellknown missing end session endpoint' ,
231+ type : 'wellknown_error' ,
232+ } as AuthorizeErrorResponse ;
233+
234+ log . error ( err . error ) ;
235+
236+ return err ;
237+ }
238+
239+ const tokens = await storageClient . get ( ) ;
240+
241+ if ( ! tokens ) {
242+ return createError ( 'no_tokens' , log ) ;
243+ }
244+
245+ if ( ! ( 'accessToken' in tokens ) ) {
246+ return createError ( 'no_access_token' , log ) ;
247+ }
248+
249+ if ( ! ( 'idToken' in tokens ) ) {
250+ return createError ( 'no_id_token' , log ) ;
251+ }
252+
253+ const logout = Micro . zip (
254+ Micro . tryPromise ( {
255+ try : ( ) =>
256+ store . dispatch (
257+ oidcApi . endpoints . endSession . initiate ( {
258+ idToken : tokens . idToken ,
259+ endpoint :
260+ wellknown . ping_end_idp_session_endpoint || wellknown . end_session_endpoint ,
261+ } ) ,
262+ ) ,
263+ catch : ( ) => {
264+ const err = {
265+ error : 'Logout request failed' ,
266+ message : 'network_error' ,
267+ } as GenericError ;
268+
269+ log . error ( err ) ;
270+
271+ return err ;
272+ } ,
273+ } ) ,
274+ Micro . tryPromise ( {
275+ try : ( ) =>
276+ store . dispatch (
277+ oidcApi . endpoints . revoke . initiate ( {
278+ accessToken : tokens . accessToken ,
279+ clientId : config . clientId ,
280+ endpoint : wellknown . revocation_endpoint ,
281+ } ) ,
282+ ) ,
283+ catch : ( ) => {
284+ const err = {
285+ error : 'Revoke request failed' ,
286+ message : 'network_error' ,
287+ } as GenericError ;
288+
289+ log . error ( err ) ;
290+
291+ return err ;
292+ } ,
293+ } ) ,
294+ ) . pipe (
295+ Micro . flatMap ( ( [ sessionResponse , revokeResponse ] ) =>
296+ Micro . gen ( function * ( ) {
297+ const deleteResponse = yield * Micro . promise ( storageClient . remove ) ;
298+ return {
299+ sessionResponse : sessionResponse ,
300+ revokeResponse : revokeResponse ,
301+ deleteResponse,
302+ } ;
303+ } ) ,
304+ ) ,
305+ ) ;
306+
307+ const result = await Micro . runPromiseExit ( logout ) ;
308+
309+ if ( exitIsSuccess ( result ) ) {
310+ await storageClient . remove ( ) ;
311+ return result . value ;
312+ } else if ( exitIsFail ( result ) ) {
313+ return result . cause . error ;
314+ } else {
315+ return {
316+ error : 'Logout failure' ,
317+ message : result . cause . message ,
318+ type : 'auth_error' ,
319+ } as GenericError ;
320+ }
321+ } ,
322+ } ,
183323 } ;
184324}
0 commit comments