Skip to content

Commit

Permalink
Register unregistered users & update obtain new token
Browse files Browse the repository at this point in the history
Signed-off-by: Fon E. Noel NFEBE <[email protected]>
  • Loading branch information
nfebe committed Sep 8, 2023
1 parent b9ab43c commit 2e89e19
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 14 deletions.
4 changes: 3 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,6 @@ PERMANENT_API_BASE_PATH=${LOCAL_TEMPORARY_AUTH_TOKEN}
# See https://fusionauth.io/docs/v1/tech/apis/api-keys
FUSION_AUTH_HOST=${FUSION_AUTH_HOST}
FUSION_AUTH_KEY=${FUSION_AUTH_KEY}
FUSION_AUTH_APP_ID=${FUSION_AUTH_APP_ID}
FUSION_AUTH_SFTP_APP_ID=${FUSION_AUTH_SFTP_APP_ID}
FUSION_AUTH_SFTP_CLIENT_ID=${FUSION_AUTH_SFTP_CLIENT_ID}
FUSION_AUTH_SFTP_CLIENT_SECRET=${FUSION_AUTH_SFTP_CLIENT_SECRET}
95 changes: 82 additions & 13 deletions src/classes/AuthenticationSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ enum FusionAuthStatusCode {
}

export class AuthenticationSession {
private static readonly sftpFusionAuthAppId = process.env.FUSION_AUTH_SFTP_APP_ID ?? '';

private static readonly sftpFusionAuthClientId = process.env.FUSION_AUTH_SFTP_CLIENT_ID ?? '';

private static readonly sftpFusionAuthClientSecret = process.env.FUSION_AUTH_SFTP_CLIENT_SECRET ?? '';

public authToken = '';

public refreshToken = '';
Expand All @@ -23,8 +29,6 @@ export class AuthenticationSession {

private readonly fusionAuthClient;

private readonly fusionAuthAppId = process.env.FUSION_AUTH_APP_ID ?? '';

private twoFactorId = '';

private twoFactorMethods: TwoFactorMethod[] = [];
Expand All @@ -39,13 +43,40 @@ export class AuthenticationSession {
}

public obtainNewAuthTokenUsingRefreshToken(): void {
this.fusionAuthClient.exchangeRefreshTokenForAccessToken(this.refreshToken, '', '', '', '')
if (!AuthenticationSession.sftpFusionAuthClientId) {
logger.error(
'Cannot obtain new access token without sftp client id.',
);
return;
}

if (!AuthenticationSession.sftpFusionAuthClientSecret) {
logger.error(
'Cannot obtain new access token without sftp client secret.',
);
return;
}

this.fusionAuthClient.exchangeRefreshTokenForAccessToken(
this.refreshToken,
AuthenticationSession.sftpFusionAuthClientId,
AuthenticationSession.sftpFusionAuthClientSecret,
'',
'',
)
.then((clientResponse) => {
this.authToken = clientResponse.response.access_token ?? '';
// The exchange refresh token for access token endpoint dose not return a timestamp,
// it returns expires_in in seconds.
// So we need to create the timestamp to be consistent with what is first
// returned upon initial authentication
this.authTokenExpiresAt = Date.now() + (clientResponse
.response.expires_in ?? 1 * 1000);
logger.info('New access token obtained');
})
.catch((clientResponse: unknown) => {
const message = isPartialClientResponse(clientResponse)
? clientResponse.exception.message
? clientResponse.exception.error_description
: '';
logger.warn(`Error obtaining refresh token : ${message}`);
this.authContext.reject();
Expand Down Expand Up @@ -77,14 +108,16 @@ export class AuthenticationSession {
}

private processPasswordResponse([password]: string[]): void {
if (!AuthenticationSession.sftpFusionAuthAppId) {
logger.error('SFTP application id missing. No refresh token would be returned');
}
this.fusionAuthClient.login({
applicationId: this.fusionAuthAppId,
applicationId: AuthenticationSession.sftpFusionAuthAppId,
loginId: this.authContext.username,
password,
}).then((clientResponse) => {
switch (clientResponse.statusCode) {
case FusionAuthStatusCode.Success:
case FusionAuthStatusCode.SuccessButUnregisteredInApp:
case FusionAuthStatusCode.Success: {
if (clientResponse.response.token !== undefined) {
logger.verbose('Successful password authentication attempt.', {
username: this.authContext.username,
Expand All @@ -93,28 +126,41 @@ export class AuthenticationSession {
this.authTokenExpiresAt = clientResponse.response.tokenExpirationInstant ?? 0;
this.refreshToken = clientResponse.response.refreshToken ?? '';
this.authContext.accept();
return;
} else {
this.authContext.reject();
}
this.authContext.reject();
return;
case FusionAuthStatusCode.SuccessNeedsTwoFactorAuth:
}
case FusionAuthStatusCode.SuccessButUnregisteredInApp: {
const userId: string = clientResponse.response.user?.id ?? '';
this.registerUserInApp(userId)
.then(() => { this.processPasswordResponse([password]); })
.catch((error) => {
logger.warn('Error during registration and authentication:', error);
this.authContext.reject();
});
return;
}
case FusionAuthStatusCode.SuccessNeedsTwoFactorAuth: {
if (clientResponse.response.twoFactorId !== undefined) {
logger.verbose('Successful password authentication attempt; MFA required.', {
username: this.authContext.username,
});
this.twoFactorId = clientResponse.response.twoFactorId;
this.twoFactorMethods = clientResponse.response.methods ?? [];
this.promptForTwoFactorMethod();
return;
} else {
this.authContext.reject();
}
this.authContext.reject();
return;
default:
}
default: {
logger.verbose('Failed password authentication attempt.', {
username: this.authContext.username,
response: clientResponse.response,
});
this.authContext.reject();
}
}
}).catch((clientResponse: unknown) => {
const message = isPartialClientResponse(clientResponse)
Expand All @@ -125,6 +171,29 @@ export class AuthenticationSession {
});
}

private async registerUserInApp(userId: string): Promise<void> {
return this.fusionAuthClient.register(userId, {
registration: {
applicationId: AuthenticationSession.sftpFusionAuthAppId,
},
}).then((clientResponse) => {
switch (clientResponse.statusCode) {
case FusionAuthStatusCode.Success:
logger.verbose('User registered successfully after authentication.', {
userId,
});
break;
default:
logger.verbose('User registration after authentication failed.', {
userId,
response: clientResponse.response,
});
}
}).catch((error) => {
logger.warn('Error during user registration after authentication:', error);
});
}

private promptForTwoFactorMethod(): void {
const promptOptions = this.twoFactorMethods.map(
(method, index) => `[${index + 1}] ${method.method ?? ''}`,
Expand Down
2 changes: 2 additions & 0 deletions src/fusionAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ export const getFusionAuthClient = (): FusionAuthClient => new FusionAuthClient(
export interface PartialClientResponse {
exception: {
message: string;
error?: string;
error_description?: string;
};
}

Expand Down

0 comments on commit 2e89e19

Please sign in to comment.