Last active
June 26, 2020 07:05
-
-
Save hakimio/be2b028d83ab4ec2f232f77e2e47a0ef to your computer and use it in GitHub Desktop.
AWS Cognito Google authentication service
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| export interface AuthenticationTokens { | |
| id_token: string; | |
| access_token: string; | |
| refresh_token: string; | |
| expires_in: number; | |
| token_type: string; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import {environment} from '../../../environments/environment'; | |
| import {Injectable} from '@angular/core'; | |
| import {HttpClient} from '@angular/common/http'; | |
| import {Observable, of} from 'rxjs'; | |
| import {AuthenticationTokens} from './google-auth.model'; | |
| import {map} from 'rxjs/operators'; | |
| import {alert} from 'devextreme/ui/dialog'; | |
| @Injectable() | |
| export class GoogleAuthService { | |
| private readonly ALREADY_FOUND_ERROR = 'Already found an entry for username'; | |
| private readonly INVALID_STATE_ERROR = 'Invalid state for'; | |
| private readonly USER_NOT_FOUND_ERROR = 'User not found'; | |
| private readonly ERROR_CODE = 'invalid_request'; | |
| private readonly OAUTH_SCOPES = ['email', 'openid', 'profile', 'aws.cognito.signin.user.admin']; | |
| constructor( | |
| private httpClient: HttpClient | |
| ) {} | |
| redirectToGoogleLogin() { | |
| const userPoolConfig = environment.cognitoUserPool, | |
| appDomain = userPoolConfig.AppWebDomain, | |
| clientId = userPoolConfig.ClientId, | |
| identityProvider = 'Google', | |
| responseType = 'code', | |
| redirectUri = encodeURIComponent(location.origin), | |
| scope = encodeURIComponent(this.OAUTH_SCOPES.join(' ')), | |
| fullUrl = `${appDomain}/oauth2/authorize` + | |
| `?redirect_uri=${redirectUri}` + | |
| `&response_type=${responseType}` + | |
| `&client_id=${clientId}` + | |
| `&identity_provider=${identityProvider}` + | |
| `&scope=${scope}`; | |
| window.location.replace(fullUrl); | |
| } | |
| getTokens(): Observable<AuthenticationTokens | null> { | |
| const searchParams = (new URL(location.href)).searchParams, | |
| errorDescription = searchParams.get('error_description'), | |
| errorCode = searchParams.get('error'), | |
| code = searchParams.get('code'); | |
| if (errorCode === this.ERROR_CODE) { | |
| this.processTheError(errorDescription); | |
| return of(null); | |
| } | |
| if (!code) { | |
| return of(null); | |
| } | |
| return this.getAuthTokens(code); | |
| } | |
| private getAuthTokens(code: string): Observable<AuthenticationTokens> { | |
| const formData = new URLSearchParams(), | |
| cognitoConfig = environment.cognitoUserPool, | |
| url = `${cognitoConfig.AppWebDomain}/oauth2/token`; | |
| formData.set('grant_type', 'authorization_code'); | |
| formData.set('client_id', cognitoConfig.ClientId); | |
| formData.set('redirect_uri', location.origin); | |
| formData.set('code', code); | |
| return this.httpClient | |
| .post(url, formData.toString(), { | |
| headers: { | |
| 'Content-Type': 'application/x-www-form-urlencoded' | |
| } | |
| }) | |
| .pipe( | |
| map( | |
| response => <AuthenticationTokens> response | |
| ) | |
| ); | |
| } | |
| private processTheError(errorDescription: string) { | |
| // Following is a workaround for AWS Cognito bug. More info: | |
| // https://stackoverflow.com/questions/47815161/cognito-auth-flow-fails-with-already-found-an-entry-for-username-facebook-10155 | |
| // https://forums.aws.amazon.com/thread.jspa?threadID=267154 | |
| // https://github.com/aws-amplify/amplify-js/issues/565 | |
| if (errorDescription.includes(this.ALREADY_FOUND_ERROR)) { | |
| this.redirectToGoogleLogin(); | |
| } else if (errorDescription.includes(this.INVALID_STATE_ERROR)) { | |
| alert('Please login normally to confirm the user', 'User not confirmed'); | |
| } else if (errorDescription.includes(this.USER_NOT_FOUND_ERROR)) { | |
| alert('Your email has to be approved by administrator first', 'Invalid email'); | |
| } else { | |
| alert('Unknown error. Please contact a developer', 'Error'); | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment