mirror of
https://github.com/bitinflow/nuxt-oauth.git
synced 2026-03-13 13:45:59 +00:00
first commit
This commit is contained in:
65
src/module.ts
Normal file
65
src/module.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import {addImportsDir, addPlugin, createResolver, defineNuxtModule} from '@nuxt/kit'
|
||||
import defu from "defu";
|
||||
|
||||
// Module options TypeScript interface definition
|
||||
export interface ModuleOptions {
|
||||
redirect: {
|
||||
login: string,
|
||||
logout: string,
|
||||
callback: string,
|
||||
home: string
|
||||
},
|
||||
endpoints: {
|
||||
authorization: string,
|
||||
userInfo: string,
|
||||
logout: string
|
||||
},
|
||||
clientId: string,
|
||||
scope: string[]
|
||||
}
|
||||
|
||||
const defaults: ModuleOptions = {
|
||||
redirect: {
|
||||
login: '/login',
|
||||
logout: '/',
|
||||
callback: '/login',
|
||||
home: '/'
|
||||
},
|
||||
endpoints: {
|
||||
authorization: 'https://accounts.bitinflow.com/oauth/authorize',
|
||||
userInfo: `https://accounts.bitinflow.com/api/v3/user`,
|
||||
logout: 'https://accounts.bitinflow.com/logout'
|
||||
},
|
||||
clientId: 'please-set-client-id',
|
||||
scope: ['user:read']
|
||||
}
|
||||
|
||||
export default defineNuxtModule<ModuleOptions>({
|
||||
meta: {
|
||||
name: '@bitinflow/nuxt-oauth',
|
||||
configKey: 'oauth'
|
||||
},
|
||||
defaults,
|
||||
setup (moduleOptions, nuxt) {
|
||||
const resolver = createResolver(import.meta.url)
|
||||
|
||||
const options = defu(moduleOptions, {
|
||||
...defaults
|
||||
})
|
||||
|
||||
// Set up runtime configuration
|
||||
nuxt.options.runtimeConfig = nuxt.options.runtimeConfig || { public: {} }
|
||||
nuxt.options.runtimeConfig.oauth = defu(nuxt.options.runtimeConfig.oauth, {
|
||||
...options
|
||||
})
|
||||
nuxt.options.runtimeConfig.public.oauth = defu(nuxt.options.runtimeConfig.public.oauth, {
|
||||
...options
|
||||
})
|
||||
|
||||
// Do not add the extension since the `.ts` will be transpiled to `.mjs` after `npm run prepack`
|
||||
addPlugin(resolver.resolve('./runtime/plugin'))
|
||||
|
||||
const composables = resolver.resolve('./runtime/composables')
|
||||
addImportsDir(composables)
|
||||
}
|
||||
})
|
||||
68
src/runtime/composables/useAuth.ts
Normal file
68
src/runtime/composables/useAuth.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import {CookieRef, navigateTo, useCookie, useRuntimeConfig} from "#app";
|
||||
import {ModuleOptions} from "../../module";
|
||||
|
||||
declare interface ComposableOptions {
|
||||
fetchUserOnInitialization: boolean
|
||||
}
|
||||
|
||||
export default async (options: ComposableOptions = {
|
||||
fetchUserOnInitialization: false
|
||||
}) => {
|
||||
const user: CookieRef<any> = useCookie('oauth_user')
|
||||
const accessToken: CookieRef<any> = useCookie('oauth_access_token')
|
||||
const authConfig = useRuntimeConfig().public.oauth as ModuleOptions;
|
||||
|
||||
const fetchUser = async () => {
|
||||
try {
|
||||
const response = await fetch(authConfig.endpoints.userInfo, {
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
Authorization: `${accessToken.value.tokenType} ${accessToken.value.token}`
|
||||
}
|
||||
});
|
||||
|
||||
user.value = response.ok
|
||||
? await response.json()
|
||||
: null;
|
||||
} catch (e) {
|
||||
user.value = null;
|
||||
}
|
||||
}
|
||||
|
||||
const signIn = async () => {
|
||||
// create oauth authorization url
|
||||
const params = new URLSearchParams({
|
||||
client_id: authConfig.clientId,
|
||||
redirect_uri: window.location.origin + authConfig.redirect.callback,
|
||||
response_type: 'token',
|
||||
scope: authConfig.scope.join(' ')
|
||||
})
|
||||
|
||||
window.location.href = `${authConfig.endpoints.authorization}?${params.toString()}`
|
||||
};
|
||||
|
||||
const signOut = async () => {
|
||||
accessToken.value = null;
|
||||
user.value = null;
|
||||
|
||||
return navigateTo('/')
|
||||
}
|
||||
|
||||
const setBearer = async (token: string, tokenType: string, expires: number) => {
|
||||
accessToken.value = {token, tokenType, expiresAt: Date.now() + expires * 1000};
|
||||
await fetchUser()
|
||||
}
|
||||
|
||||
// Initialize the user if the option is set to true
|
||||
if (options.fetchUserOnInitialization) {
|
||||
await fetchUser()
|
||||
}
|
||||
|
||||
return {
|
||||
user,
|
||||
signIn,
|
||||
signOut,
|
||||
setBearer,
|
||||
authConfig
|
||||
}
|
||||
}
|
||||
35
src/runtime/plugin.ts
Normal file
35
src/runtime/plugin.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import {addRouteMiddleware, defineNuxtPlugin, navigateTo} from '#app'
|
||||
import useAuth from "./composables/useAuth"
|
||||
|
||||
export default defineNuxtPlugin(() => {
|
||||
addRouteMiddleware('auth', async (to) => {
|
||||
const {user, authConfig, setBearer} = await useAuth()
|
||||
|
||||
if (to.path === authConfig.redirect.callback) {
|
||||
const params = new URLSearchParams(to.hash.substring(1))
|
||||
|
||||
if (params.has('access_token')) {
|
||||
const token = params.get('access_token') as string;
|
||||
const tokenType = params.get('token_type') as string;
|
||||
const expires = params.get('expires_in') as string;
|
||||
|
||||
await setBearer(token, tokenType, parseInt(expires));
|
||||
return navigateTo(authConfig.redirect.home)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (user.value === undefined) {
|
||||
return navigateTo(authConfig.redirect.login)
|
||||
}
|
||||
})
|
||||
|
||||
addRouteMiddleware('guest', async () => {
|
||||
const {user, authConfig} = await useAuth()
|
||||
|
||||
if (user.value !== undefined) {
|
||||
return navigateTo(authConfig.redirect.home)
|
||||
}
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user