From 1cb448bad126ca7e41570986b632f6cc70639b24 Mon Sep 17 00:00:00 2001 From: Danny August Ramaputra Date: Wed, 20 Jan 2021 09:52:55 +1000 Subject: [PATCH] Migrated authmanager to Ratify client library --- ratify-fe/package.json | 5 +- ratify-fe/src/apis/api.ts | 2 +- ratify-fe/src/auth/AuthManager.ts | 179 ------------------------------ ratify-fe/src/auth/index.ts | 4 +- ratify-fe/yarn.lock | 18 ++- 5 files changed, 21 insertions(+), 187 deletions(-) delete mode 100644 ratify-fe/src/auth/AuthManager.ts diff --git a/ratify-fe/package.json b/ratify-fe/package.json index d81b0ba..f6b7762 100644 --- a/ratify-fe/package.json +++ b/ratify-fe/package.json @@ -8,14 +8,11 @@ "lint": "vue-cli-service lint" }, "dependencies": { + "@daystram/ratify-client": "^1.2.0", "axios": "^0.21.1", "core-js": "^3.6.5", - "jwt-decode": "^3.1.2", - "pkce-challenge": "^2.1.0", "qrcode.vue": "^3.1.0", - "querystring": "^0.2.0", "register-service-worker": "^1.7.1", - "uuid": "^8.3.2", "vue": "^2.6.11", "vue-class-component": "^7.2.3", "vue-property-decorator": "^8.4.2", diff --git a/ratify-fe/src/apis/api.ts b/ratify-fe/src/apis/api.ts index 658ad64..6720305 100644 --- a/ratify-fe/src/apis/api.ts +++ b/ratify-fe/src/apis/api.ts @@ -1,6 +1,6 @@ import axios, { AxiosInstance, AxiosResponse } from "axios"; +import { ACCESS_TOKEN } from "@daystram/ratify-client"; import { authManager, refreshAuth } from "@/auth"; -import { ACCESS_TOKEN } from "@/auth/AuthManager"; import router from "@/router"; const apiClient: AxiosInstance = axios.create({ diff --git a/ratify-fe/src/auth/AuthManager.ts b/ratify-fe/src/auth/AuthManager.ts deleted file mode 100644 index fcc9e5f..0000000 --- a/ratify-fe/src/auth/AuthManager.ts +++ /dev/null @@ -1,179 +0,0 @@ -import qs from "qs"; -import pkceChallenge from "pkce-challenge"; -import { v4 as uuidv4 } from "uuid"; -import axios, { AxiosResponse } from "axios"; -import jwtDecode from "jwt-decode"; - -export const KEY_STATE = "state"; -export const KEY_CODE = "code"; -export const KEY_TOKEN = "token"; -export const ACCESS_TOKEN = "access_token"; -export const ID_TOKEN = "id_token"; - -interface AuthManagerOptions { - clientId: string; - redirectUri: string; - issuer: string; - storage: TokenStorage; -} - -interface OAuthClient { - token: (tokenRequest: object) => Promise; - logout: (logoutRequest: object) => Promise; -} - -interface User { - subject: string; - given_name: string; - family_name: string; - preferred_username: string; - is_superuser: boolean; -} - -interface TokenStorage { - getItem: (key: string) => string | null; - setItem: (key: string, value: string) => void; - removeItem: (key: string) => void; -} - -export class MemoryStorage implements TokenStorage { - private tokens: { [key: string]: string }; - - constructor() { - this.tokens = {}; - } - - getItem(key: string): string | null { - return this.tokens[key]; - } - - setItem(key: string, value: string): void { - this.tokens[key] = value; - } - - removeItem(key: string): void { - delete this.tokens[key]; - } -} - -export class AuthManager { - private options: AuthManagerOptions; - private storageManager: TokenStorage; - private oauth: OAuthClient; - - constructor(opts: AuthManagerOptions) { - this.options = opts; - this.storageManager = opts.storage; - // code and state will still use sessionStorage (need to persist during page reloads) - const oauthClient = axios.create({ - baseURL: `${this.options.issuer}/oauth/` - }); - this.oauth = { - token: function(tokenRequest: object): Promise { - return oauthClient.post(`token`, qs.stringify(tokenRequest), { - headers: { - "Content-Type": "application/x-www-form-urlencoded" - } - }); - }, - logout: function(logoutRequest: object): Promise { - return oauthClient.post(`logout`, qs.stringify(logoutRequest), { - headers: { - "Content-Type": "application/x-www-form-urlencoded" - } - }); - } - }; - } - - isAuthenticated(): boolean { - return this.getToken(ACCESS_TOKEN) !== ""; - } - - getToken(tokenKey: string): string { - return ( - JSON.parse(this.storageManager.getItem(KEY_TOKEN) || "{}")[tokenKey] || "" - ); - } - - getUser(): User { - return jwtDecode(this.getToken(ID_TOKEN)); - } - - reset() { - this.storageManager.removeItem(KEY_TOKEN); - } - - authorize(immediate?: boolean, scopes?: string[]): void { - window.location.href = `${this.options.issuer}/authorize?${qs.stringify({ - /* eslint-disable @typescript-eslint/camelcase */ - client_id: this.options.clientId, - response_type: "code", - redirect_uri: this.options.redirectUri, - scope: - "openid profile" + (scopes || []).map(scope => " " + scope).join(""), - state: this.getState(), - code_challenge: this.getCodeChallenge(), - code_challenge_method: "S256", - immediate: immediate || false - /* eslint-enable @typescript-eslint/camelcase */ - })}`; - } - - redeemToken(authorizationCode: string): Promise { - return this.oauth - .token({ - /* eslint-disable @typescript-eslint/camelcase */ - client_id: this.options.clientId, - grant_type: "authorization_code", - code: authorizationCode, - code_verifier: this.getCodeVerifier() - /* eslint-enable @typescript-eslint/camelcase */ - }) - .then(response => { - this.storageManager.setItem(KEY_TOKEN, JSON.stringify(response.data)); - return response; - }); - } - - logout(global?: boolean) { - return this.oauth - .logout({ - /* eslint-disable @typescript-eslint/camelcase */ - access_token: this.getToken(ACCESS_TOKEN), - client_id: this.options.clientId, - global: global || false - /* eslint-enable @typescript-eslint/camelcase */ - }) - .then(() => { - this.reset(); - }) - .catch(() => { - this.reset(); - }); - } - - getState(): string { - const state = uuidv4(); - sessionStorage.setItem(KEY_STATE, state); - return state; - } - - checkState(state: string): boolean { - const temp = sessionStorage.getItem(KEY_STATE); - sessionStorage.removeItem(KEY_STATE); - return temp === state; - } - - getCodeChallenge() { - const pkce = pkceChallenge(); - sessionStorage.setItem(KEY_CODE, JSON.stringify(pkce)); - return pkce.code_challenge; - } - - getCodeVerifier() { - const pkce = JSON.parse(sessionStorage.getItem(KEY_CODE) || ""); - sessionStorage.removeItem(KEY_CODE); - return pkce.code_verifier; - } -} diff --git a/ratify-fe/src/auth/index.ts b/ratify-fe/src/auth/index.ts index ccac94a..95d7ee2 100644 --- a/ratify-fe/src/auth/index.ts +++ b/ratify-fe/src/auth/index.ts @@ -1,12 +1,12 @@ +import { ACCESS_TOKEN, RatifyClient } from "@daystram/ratify-client"; import router from "@/router"; -import { AuthManager, ACCESS_TOKEN } from "@/auth/AuthManager"; import { Route } from "vue-router"; const CLIENT_ID = process.env.VUE_APP_CLIENT_ID; const ISSUER = process.env.VUE_APP_OAUTH_ISSUER; const REDIRECT_URI = `${location.origin}/callback`; -const authManager = new AuthManager({ +const authManager = new RatifyClient({ clientId: CLIENT_ID, redirectUri: REDIRECT_URI, issuer: ISSUER, diff --git a/ratify-fe/yarn.lock b/ratify-fe/yarn.lock index ae609fe..789674b 100644 --- a/ratify-fe/yarn.lock +++ b/ratify-fe/yarn.lock @@ -846,6 +846,17 @@ lodash "^4.17.19" to-fast-properties "^2.0.0" +"@daystram/ratify-client@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@daystram/ratify-client/-/ratify-client-1.2.0.tgz#46253c3e866d60c255055e00eb5193ef4ad4000c" + integrity sha512-DqeQbGJSaDPZQ9DfixEweebOLp4p7QktzSw4r/K69FKuYoWeWZbR8ZtgilpF2yeGZj9ultT+CdxAEJc7MO8CKA== + dependencies: + axios "^0.21.1" + jwt-decode "^3.1.2" + pkce-challenge "^2.1.0" + qs "^6.9.6" + uuid "^8.3.2" + "@hapi/address@2.x.x": version "2.1.4" resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5" @@ -7041,6 +7052,11 @@ qs@6.7.0: resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== +qs@^6.9.6: + version "6.9.6" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.6.tgz#26ed3c8243a431b2924aca84cc90471f35d5a0ee" + integrity sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ== + qs@~6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" @@ -7059,7 +7075,7 @@ querystring-es3@^0.2.0: resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM= -querystring@0.2.0, querystring@^0.2.0: +querystring@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=