UNPKG

@lucasroll62/nuxt3-auth

Version:

An alternative module to @nuxtjs/auth

187 lines (186 loc) 6.05 kB
import { Oauth2Scheme } from "./oauth2.mjs"; import { normalizePath, getProp, parseQuery } from "../../utils"; import { IdToken, ConfigurationDocument } from "../inc/index.mjs"; import { useRoute } from "#imports"; import { withQuery } from "ufo"; const DEFAULTS = { name: "openIDConnect", responseType: "code", grantType: "authorization_code", scope: ["openid", "profile", "offline_access"], idToken: { property: "id_token", maxAge: 1800, prefix: "_id_token.", expirationPrefix: "_id_token_expiration." }, fetchRemote: false, codeChallengeMethod: "S256" }; export class OpenIDConnectScheme extends Oauth2Scheme { constructor($auth, options, ...defaults) { super($auth, options, ...defaults, DEFAULTS); this.idToken = new IdToken(this, this.$auth.$storage); this.configurationDocument = new ConfigurationDocument(this, this.$auth.$storage); } updateTokens(response) { super.updateTokens(response); const idToken = getProp(response, this.options.idToken.property); if (idToken) { this.idToken.set(idToken); } } check(checkStatus = false) { const response = { valid: false, tokenExpired: false, refreshTokenExpired: false, idTokenExpired: false, isRefreshable: true }; const token = this.token.sync(); this.refreshToken.sync(); this.idToken.sync(); if (!token) { return response; } if (!checkStatus) { response.valid = true; return response; } const tokenStatus = this.token.status(); const refreshTokenStatus = this.refreshToken.status(); const idTokenStatus = this.idToken.status(); if (refreshTokenStatus.expired()) { response.refreshTokenExpired = true; return response; } if (tokenStatus.expired()) { response.tokenExpired = true; return response; } if (idTokenStatus.expired()) { response.idTokenExpired = true; return response; } response.valid = true; return response; } async mounted() { await this.configurationDocument.init(); const { tokenExpired, refreshTokenExpired } = this.check(true); if (refreshTokenExpired || tokenExpired && this.options.autoLogout) { this.$auth.reset(); } this.requestHandler.initializeRequestInterceptor(this.options.endpoints.token); const redirected = await this.#handleCallback(); if (!redirected) { return this.$auth.fetchUserOnce(); } } reset() { this.$auth.setUser(false); this.token.reset(); this.idToken.reset(); this.refreshToken.reset(); this.requestHandler.reset(); this.configurationDocument.reset(); } logout() { if (this.options.endpoints.logout) { const opts = { id_token_hint: this.idToken.get(), post_logout_redirect_uri: this.logoutRedirectURI }; const url = withQuery(this.options.endpoints.logout, opts); window.location.replace(url); } return this.$auth.reset(); } async fetchUser() { if (!this.check().valid) { return; } if (!this.options.fetchRemote && this.idToken.get()) { const data2 = this.idToken.userInfo(); this.$auth.setUser(data2); return; } if (!this.options.endpoints.userInfo) { this.$auth.setUser({}); return; } const data = await this.$auth.requestWith({ url: this.options.endpoints.userInfo }); this.$auth.setUser(data); } async #handleCallback() { const route = useRoute(); if (this.$auth.options.redirect && normalizePath(route.path) !== normalizePath(this.$auth.options.redirect.callback)) { return; } if (process.server) { return; } const hash = parseQuery(route.hash.slice(1)); const parsedQuery = Object.assign({}, route.query, hash); let token = parsedQuery[this.options.token.property]; let refreshToken; if (this.options.refreshToken.property) { refreshToken = parsedQuery[this.options.refreshToken.property]; } let idToken = parsedQuery[this.options.idToken.property]; const state = this.$auth.$storage.getUniversal(this.name + ".state"); this.$auth.$storage.setUniversal(this.name + ".state", null); if (state && parsedQuery.state !== state) { return; } if (this.options.responseType.includes("code") && parsedQuery.code) { let codeVerifier; if (this.options.codeChallengeMethod && this.options.codeChallengeMethod !== "implicit") { codeVerifier = this.$auth.$storage.getUniversal(this.name + ".pkce_code_verifier"); this.$auth.$storage.setUniversal(this.name + ".pkce_code_verifier", null); } const response = await this.$auth.request({ method: "post", url: this.options.endpoints.token, baseURL: "", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: new URLSearchParams({ code: parsedQuery.code, client_id: this.options.clientId, redirect_uri: this.redirectURI, response_type: this.options.responseType, audience: this.options.audience, grant_type: this.options.grantType, code_verifier: codeVerifier }) }); token = getProp(response, this.options.token.property) || token; refreshToken = getProp(response, this.options.refreshToken.property) || refreshToken; idToken = getProp(response, this.options.idToken.property) || idToken; } if (!token || !token.length) { return; } this.token.set(token); if (refreshToken && refreshToken.length) { this.refreshToken.set(refreshToken); } if (idToken && idToken.length) { this.idToken.set(idToken); } if (this.options.clientWindow) { if (globalThis.opener) { globalThis.opener.postMessage({ isLoggedIn: true }); globalThis.close(); } } else if (this.$auth.options.watchLoggedIn) { this.$auth.redirect("home", false, false); return true; } } }