@@ -16,7 +16,12 @@ import {
1616 ConfirmSignUpCommandOutput ,
1717 ResendConfirmationCodeCommandOutput ,
1818} from '@aws-sdk/client-cognito-identity-provider' ;
19- import { COGNITO_CONFIG } from '../../config/aws-config' ;
19+ import {
20+ CognitoIdentityClient ,
21+ GetIdCommand ,
22+ GetCredentialsForIdentityCommand ,
23+ } from '@aws-sdk/client-cognito-identity' ;
24+ import { COGNITO_CONFIG , REGION } from '../../config/aws-config' ;
2025import { UserTokens } from '../../models/auth' ;
2126import { CognitoUser } from '../../models/user' ;
2227
@@ -42,6 +47,7 @@ interface CognitoSession {
4247 */
4348export class DirectCognitoAuthService {
4449 private static client = new CognitoIdentityProviderClient ( { region : 'us-east-1' } ) ;
50+ private static identityClient = new CognitoIdentityClient ( { region : REGION } ) ;
4551 private static clientId = COGNITO_CONFIG . USER_POOL_WEB_CLIENT_ID ;
4652 private static userPoolId = COGNITO_CONFIG . USER_POOL_ID ;
4753
@@ -413,35 +419,156 @@ export class DirectCognitoAuthService {
413419 }
414420
415421 /**
416- * Fetches the current authentication session, similar to Amplify's fetchAuthSession
422+ * Refreshes the token using the refresh token if available
423+ * @param tokens Current tokens
424+ * @returns Updated tokens if refresh succeeded, original tokens otherwise
425+ */
426+ private static async refreshTokensIfNeeded ( tokens : UserTokens ) : Promise < UserTokens > {
427+ // Check if token is about to expire (within 5 minutes)
428+ const needsRefresh = tokens . expires_at
429+ ? new Date ( tokens . expires_at ) . getTime ( ) - Date . now ( ) < 5 * 60 * 1000
430+ : false ;
431+
432+ // If token doesn't need refresh or we don't have a refresh token, return original tokens
433+ if ( ! needsRefresh || ! tokens . refresh_token ) {
434+ return tokens ;
435+ }
436+
437+ try {
438+ const refreshParams = {
439+ AuthFlow : 'REFRESH_TOKEN_AUTH' as AuthFlowType ,
440+ ClientId : this . clientId ,
441+ AuthParameters : {
442+ REFRESH_TOKEN : tokens . refresh_token ,
443+ } ,
444+ } ;
445+
446+ const refreshCommand = new InitiateAuthCommand ( refreshParams ) ;
447+ const refreshResponse = await this . client . send ( refreshCommand ) ;
448+
449+ if ( ! refreshResponse . AuthenticationResult ) {
450+ return tokens ; // No auth result, return original tokens
451+ }
452+
453+ const { IdToken, AccessToken, ExpiresIn } = refreshResponse . AuthenticationResult ;
454+
455+ if ( ! IdToken || ! AccessToken ) {
456+ return tokens ; // Missing tokens, return original tokens
457+ }
458+
459+ // Update tokens in local storage
460+ localStorage . setItem ( 'cognito_id_token' , IdToken ) ;
461+ localStorage . setItem ( 'cognito_access_token' , AccessToken ) ;
462+
463+ // Return updated tokens
464+ return {
465+ ...tokens ,
466+ id_token : IdToken ,
467+ access_token : AccessToken ,
468+ expires_in : ExpiresIn || 3600 ,
469+ expires_at : new Date ( Date . now ( ) + ( ExpiresIn || 3600 ) * 1000 ) . toISOString ( ) ,
470+ } ;
471+ } catch ( error ) {
472+ console . warn ( 'Token refresh failed, proceeding with existing token:' , error ) ;
473+ return tokens ; // Return original tokens on error
474+ }
475+ }
476+
477+ /**
478+ * Get Cognito Identity ID
479+ * @param tokens User tokens
480+ * @returns The Identity ID
481+ */
482+ private static async getIdentityId ( tokens : UserTokens ) : Promise < string > {
483+ const identityPoolId = COGNITO_CONFIG . IDENTITY_POOL_ID ;
484+
485+ if ( ! identityPoolId ) {
486+ throw new Error ( 'Identity Pool ID is not configured' ) ;
487+ }
488+
489+ const getIdParams = {
490+ IdentityPoolId : identityPoolId ,
491+ Logins : {
492+ [ `cognito-idp.${ REGION } .amazonaws.com/${ this . userPoolId } ` ] : tokens . id_token ,
493+ } ,
494+ } ;
495+
496+ const getIdCommand = new GetIdCommand ( getIdParams ) ;
497+ const identityResponse = await this . identityClient . send ( getIdCommand ) ;
498+
499+ if ( ! identityResponse . IdentityId ) {
500+ throw new Error ( 'Failed to obtain identity ID' ) ;
501+ }
502+
503+ return identityResponse . IdentityId ;
504+ }
505+
506+ /**
507+ * Get AWS credentials using Identity ID
508+ * @param identityId The Identity ID
509+ * @param idToken The ID token
510+ * @returns AWS credentials
511+ */
512+ private static async getAWSCredentials (
513+ identityId : string ,
514+ idToken : string ,
515+ ) : Promise < AWSCredentials > {
516+ const credentialsParams = {
517+ IdentityId : identityId ,
518+ Logins : {
519+ [ `cognito-idp.${ REGION } .amazonaws.com/${ this . userPoolId } ` ] : idToken ,
520+ } ,
521+ } ;
522+
523+ const credentialsCommand = new GetCredentialsForIdentityCommand ( credentialsParams ) ;
524+ const credentialsResponse = await this . identityClient . send ( credentialsCommand ) ;
525+
526+ if ( ! credentialsResponse . Credentials ) {
527+ throw new Error ( 'Failed to obtain AWS credentials' ) ;
528+ }
529+
530+ const { AccessKeyId, SecretKey, SessionToken, Expiration } = credentialsResponse . Credentials ;
531+
532+ if ( ! AccessKeyId || ! SecretKey || ! SessionToken || ! Expiration ) {
533+ throw new Error ( 'Incomplete AWS credentials received' ) ;
534+ }
535+
536+ return {
537+ accessKeyId : AccessKeyId ,
538+ secretAccessKey : SecretKey ,
539+ sessionToken : SessionToken ,
540+ expiration : Expiration ,
541+ } ;
542+ }
543+
544+ /**
545+ * Fetches the current authentication session, including AWS credentials
417546 * @returns Promise with session information including credentials
418547 */
419548 static async fetchAuthSession ( ) : Promise < { credentials : AWSCredentials } > {
420549 try {
550+ // Get the current tokens
421551 const tokens = this . getTokens ( ) ;
422- if ( ! tokens || ! tokens . access_token ) {
552+ if ( ! tokens || ! tokens . id_token ) {
423553 throw new Error ( 'No active session found' ) ;
424554 }
425555
426- // In a full implementation, you would exchange these tokens for AWS credentials
427- // using Cognito Identity Pool. For now, we'll return a simplified structure.
556+ // Refresh tokens if needed
557+ const refreshedTokens = await this . refreshTokensIfNeeded ( tokens ) ;
428558
429- // Calculate an expiration time based on the token
430- const expiresAt = tokens . expires_at
431- ? new Date ( tokens . expires_at )
432- : new Date ( Date . now ( ) + 3600 * 1000 ) ;
559+ // Get identity ID
560+ const identityId = await this . getIdentityId ( refreshedTokens ) ;
433561
434- return {
435- credentials : {
436- accessKeyId : process . env . AWS_ACCESS_KEY_ID || 'DEMO_ACCESS_KEY_ID' ,
437- secretAccessKey : process . env . AWS_SECRET_ACCESS_KEY || 'DEMO_SECRET_ACCESS_KEY' ,
438- sessionToken : tokens . id_token ,
439- expiration : expiresAt ,
440- } ,
441- } ;
562+ // Get AWS credentials
563+ const credentials = await this . getAWSCredentials ( identityId , refreshedTokens . id_token ) ;
564+
565+ return { credentials } ;
442566 } catch ( error ) {
443567 console . error ( 'Error fetching auth session:' , error ) ;
444- throw error ;
568+ throw new Error (
569+ 'Failed to get authentication session: ' +
570+ ( error instanceof Error ? error . message : String ( error ) ) ,
571+ ) ;
445572 }
446573 }
447574
0 commit comments