You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Currently, Foal allows users to be authenticated through social providers. Through social services, the application can authenticate users with Google, for example, and in return get user information as well as access and refresh tokens to communicate with the Google API.
Issue 1
However, there is no "easy" way to store securely the refresh token for later communication with the API. We can store the refresh token in the session using ctx.session.set('refreshToken', tokens.refresh_token) but then the token is saved in plain text in the database. This can cause security problems if the database is corrupted. For this reason, Foal should provide an easy way to encrypt and decrypt refresh tokens.
Issue 2
Once the refresh token is retrieved, in a later request for example, we still need to take a look at the Google API (or other providers API) to see how to get a new access token. This task could simply be handled by the framework.
Possible solution
Add three new methods encryptRefreshToken, decryptRefreshToken and refreshAndGetAccessToken to each social service.
exportclassAuthController{
@dependencygoogle: GoogleProvider;
@dependencystore: Store;
@Get('/signin/google')redirectToGoogle(){returnthis.google.redirect({scopes: ['email','openid','profile',// Request access to view the user's YouTube account.'https://www.googleapis.com/auth/youtube.readonly']});}
@Get('/signin/google/callback')
@UseSessions({cookie: true,csrf: false,})asynchandleGoogleRedirection(ctx: Context){const{ userInfo }=awaitthis.google.getUserInfo(ctx);if(!userInfo.email){thrownewError('Google should have returned an email address.');}letuser=awaitUser.findOne({email: userInfo.email});if(!user){// If the user has not already signed up, then add them to the database.user=newUser();user.email=userInfo.email;awaituser.save();}ctx.session.setUser(user);ctx.session.set('refreshToken',this.google.encryptRefreshToken(tokens.refresh_token))returnnewHttpResponseRedirect('/');}}
@LoicPoullain Hi! I remember that i've made two methods for encrypt and decrypt a string using a secret from config in AbstractProvider. This could be used for solving issue 1. 👍
/** * This function is for encrypt a string using aes-256 and codeVerifierSecret. * Notice that init vector base64-encoded is concatenated at start of encrypted message. * We'll need init vector to decrypt message. * Init vector is 16 bytes length and it base64-encoded is 24 bytes length. * * @param {string} message - String to encrypt */privateencryptString(message: string): string {consthashedSecret=this.getCodeVerifierSecretBuffer();// Initiate iv with random bytesconstinitVector=crypto.randomBytes(16);// Create cipherconstcipher=crypto.createCipheriv(this.cryptAlgorithm,hashedSecret,initVector);// Encrypt data, concat finalconstdata=cipher.update(Buffer.from(message));constencryptedMessage=Buffer.concat([data,cipher.final()])return`${initVector.toString('base64')}${encryptedMessage.toString('base64')}`}/** * This function is for decrypt a string using aes-256 and codeVerifierSecret * encryptedMessage is {iv}{encrypted data} * * @param {string} encryptedMessage - String to decrypt */privatedecryptString(encryptedMessage: string): string {consthashedSecret=this.getCodeVerifierSecretBuffer();// Get init vector back from encryptedMessageconstinitVector: Buffer=Buffer.from(encryptedMessage.substring(0,24),'base64');// original iv is 16 bytes long, so base64 encoded is 24 bytes longconstmessage: string=encryptedMessage.substring(24);// Create decipherconstdecipher=crypto.createDecipheriv(this.cryptAlgorithm,hashedSecret,initVector);// Decrypt data, concat finalconstdata=decipher.update(Buffer.from(message,'base64'));constdecryptedMessage=Buffer.concat([data,decipher.final()])returndecryptedMessage.toString()}privategetCodeVerifierSecretBuffer(): Buffer{// Get secret from config file or throw an error if not definedconstcodeVerifierSecret=Config.getOrThrow(this.codeVerifierSecretPath,'string');// We create a sha256 hash to ensure that key is 32 bytes longreturncrypto.createHash('sha256').update(codeVerifierSecret).digest();}```
Issue
Currently, Foal allows users to be authenticated through social providers. Through social services, the application can authenticate users with Google, for example, and in return get user information as well as access and refresh tokens to communicate with the Google API.
Issue 1
However, there is no "easy" way to store securely the refresh token for later communication with the API. We can store the refresh token in the session using
ctx.session.set('refreshToken', tokens.refresh_token)
but then the token is saved in plain text in the database. This can cause security problems if the database is corrupted. For this reason, Foal should provide an easy way to encrypt and decrypt refresh tokens.Issue 2
Once the refresh token is retrieved, in a later request for example, we still need to take a look at the Google API (or other providers API) to see how to get a new access token. This task could simply be handled by the framework.
Possible solution
Add three new methods
encryptRefreshToken
,decryptRefreshToken
andrefreshAndGetAccessToken
to each social service.The text was updated successfully, but these errors were encountered: