mirror of
https://github.com/bitinflow/nuxt-oauth.git
synced 2026-03-13 13:45:59 +00:00
@@ -1,8 +1,17 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## v2.0.4
|
||||||
|
|
||||||
|
Add `cookies` option in `ModuleOptions` interface
|
||||||
|
|
||||||
|
## v2.0.3
|
||||||
|
|
||||||
|
Add ", { external: true }" in navigateTo
|
||||||
|
|
||||||
## v2.0.2
|
## v2.0.2
|
||||||
|
|
||||||
|
Fix for `auth` middleware
|
||||||
|
|
||||||
## v2.0.1
|
## v2.0.1
|
||||||
|
|
||||||
Typo fixes in the GitHub/NPM repo
|
Typo fixes in the GitHub/NPM repo
|
||||||
|
|||||||
67
README.md
67
README.md
@@ -54,7 +54,7 @@ export default defineNuxtConfig({
|
|||||||
'/whatever/**': {ssr: false}
|
'/whatever/**': {ssr: false}
|
||||||
},
|
},
|
||||||
|
|
||||||
// using code response type (default)
|
// example 1: using code response type (default)
|
||||||
oauth: {
|
oauth: {
|
||||||
endpoints: {
|
endpoints: {
|
||||||
authorization: 'https://example.com/oauth/authorize',
|
authorization: 'https://example.com/oauth/authorize',
|
||||||
@@ -66,7 +66,7 @@ export default defineNuxtConfig({
|
|||||||
scope: ['user:read']
|
scope: ['user:read']
|
||||||
},
|
},
|
||||||
|
|
||||||
// using token response type (not recommended)
|
// example 2: using token response type (not recommended)
|
||||||
oauth: {
|
oauth: {
|
||||||
endpoints: {
|
endpoints: {
|
||||||
authorization: 'https://example.com/oauth/authorize',
|
authorization: 'https://example.com/oauth/authorize',
|
||||||
@@ -86,6 +86,69 @@ This will be your callback url (host is determined by `window.location.origin`):
|
|||||||
|
|
||||||
That's it! You can now use @bitinflow/nuxt-oauth in your Nuxt app ✨
|
That's it! You can now use @bitinflow/nuxt-oauth in your Nuxt app ✨
|
||||||
|
|
||||||
|
## Module Options
|
||||||
|
|
||||||
|
The module provides a set of customizable options to configure OAuth-based authentication for your application. Below is a detailed description of each option and its default values:
|
||||||
|
|
||||||
|
### `redirect`
|
||||||
|
|
||||||
|
This option defines the URLs for redirection during the authentication process.
|
||||||
|
|
||||||
|
- `login` (`string`): The URL to redirect to when a user needs to log in. Default: `/login`.
|
||||||
|
- `logout` (`string`): The URL to redirect to after logging out. Default: `/`.
|
||||||
|
- `callback` (`string`): The URL to handle the OAuth callback. Default: `/login`.
|
||||||
|
- `home` (`string`): The URL to redirect to after successful authentication. Default: `/`.
|
||||||
|
|
||||||
|
### `endpoints`
|
||||||
|
|
||||||
|
Configures the OAuth server endpoints for authorization, token exchange, and user information retrieval.
|
||||||
|
|
||||||
|
- `authorization` (`string`): The OAuth authorization endpoint. Default: `https://example.com/oauth/authorize`.
|
||||||
|
- `token` (`string`): The OAuth token endpoint. Default: `https://example.com/oauth/token`.
|
||||||
|
- `userInfo` (`string`): The endpoint to retrieve user information. Default: `https://example.com/api/users/me`.
|
||||||
|
- `logout` (`string | null`): The endpoint for logging out from the OAuth provider. Default: `null`.
|
||||||
|
|
||||||
|
### `refreshToken`
|
||||||
|
|
||||||
|
Manages the refresh token settings.
|
||||||
|
|
||||||
|
- `maxAge` (`number`): The maximum age (in seconds) for storing the refresh token in cookies. Default: `60 * 60 * 24 * 30` (30 days).
|
||||||
|
|
||||||
|
### `cookies`
|
||||||
|
|
||||||
|
Configures cookie settings for storing OAuth tokens and related data.
|
||||||
|
|
||||||
|
- `prefix` (`string`): A prefix for all cookie names. Default: none.
|
||||||
|
- `names`: Specific names for different OAuth-related cookies.
|
||||||
|
- `oauth_user`: The cookie name for storing the OAuth user. Default: `oauth_user`.
|
||||||
|
- `oauth_state`: The cookie name for storing the OAuth state. Default: `oauth_state`.
|
||||||
|
- `oauth_code_verifier`: The cookie name for storing the OAuth code verifier. Default: `oauth_code_verifier`.
|
||||||
|
- `oauth_access_token`: The cookie name for storing the access token. Default: `oauth_access_token`.
|
||||||
|
- `oauth_refresh_token`: The cookie name for storing the refresh token. Default: `oauth_refresh_token`.
|
||||||
|
- `options`: Additional settings for cookie behavior.
|
||||||
|
- `path` (`string`): The cookie path. Default: none.
|
||||||
|
- `maxAge` (`number`): The cookie's maximum age (in seconds). Default: none.
|
||||||
|
- `secure` (`boolean`): Whether the cookie should only be sent over HTTPS. Default: none.
|
||||||
|
- `sameSite` (`string`): Sets the `SameSite` cookie attribute (`lax`, `strict`, or `none`). Default: none.
|
||||||
|
- `domain` (`string`): Specifies the cookie's domain. Default: none.
|
||||||
|
- `httpOnly` (`boolean`): Indicates if the cookie is inaccessible to JavaScript. Default: none.
|
||||||
|
|
||||||
|
### `clientId`
|
||||||
|
|
||||||
|
- (`string`): The client ID used for OAuth authentication. Default: `please-set-client-id`.
|
||||||
|
|
||||||
|
### `responseType`
|
||||||
|
|
||||||
|
- (`'token' | 'code'`): The type of OAuth response, either token-based or code-based flow. Default: `code`.
|
||||||
|
|
||||||
|
### `prompt`
|
||||||
|
|
||||||
|
- (`'' | 'none' | 'login' | 'consent'`): The prompt parameter to control the OAuth flow. Default: `''`.
|
||||||
|
|
||||||
|
### `scope`
|
||||||
|
|
||||||
|
- (`string[]`): The OAuth scopes requested during authentication. Default: `[]` (empty array).
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@bitinflow/nuxt-oauth",
|
"name": "@bitinflow/nuxt-oauth",
|
||||||
"version": "2.0.3",
|
"version": "2.0.4",
|
||||||
"description": "Nuxt 3 OAuth Module",
|
"description": "Nuxt 3 OAuth Module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
"@nuxt/module-builder": "^0.2.1",
|
"@nuxt/module-builder": "^0.2.1",
|
||||||
"@nuxt/schema": "^3.2.2",
|
"@nuxt/schema": "^3.2.2",
|
||||||
"@nuxt/test-utils": "^3.2.2",
|
"@nuxt/test-utils": "^3.2.2",
|
||||||
"axios": "^1.3.5",
|
"axios": "^1.6.0",
|
||||||
"changelogen": "^0.4.1",
|
"changelogen": "^0.4.1",
|
||||||
"eslint": "^8.34.0",
|
"eslint": "^8.34.0",
|
||||||
"nuxt": "^3.2.2",
|
"nuxt": "^3.2.2",
|
||||||
|
|||||||
@@ -7,6 +7,11 @@ export default defineNuxtConfig({
|
|||||||
redirect: {
|
redirect: {
|
||||||
home: '/home'
|
home: '/home'
|
||||||
},
|
},
|
||||||
|
cookies: {
|
||||||
|
options: {
|
||||||
|
domain: '*.own3d.pro'
|
||||||
|
}
|
||||||
|
},
|
||||||
clientId: '98e1cb74-125a-4d60-b686-02c2f0c87521',
|
clientId: '98e1cb74-125a-4d60-b686-02c2f0c87521',
|
||||||
scope: ['user:read']
|
scope: ['user:read']
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -18,6 +18,24 @@ export interface ModuleOptions {
|
|||||||
refreshToken?: {
|
refreshToken?: {
|
||||||
maxAge: number,
|
maxAge: number,
|
||||||
}
|
}
|
||||||
|
cookies?: {
|
||||||
|
prefix?: string,
|
||||||
|
names?: {
|
||||||
|
oauth_user?: 'oauth_user'
|
||||||
|
oauth_state?: 'oauth_state'
|
||||||
|
oauth_code_verifier?: 'oauth_code_verifier'
|
||||||
|
oauth_access_token?: 'oauth_access_token'
|
||||||
|
oauth_refresh_token?: 'oauth_refresh_token'
|
||||||
|
}
|
||||||
|
options?: {
|
||||||
|
path?: string,
|
||||||
|
maxAge?: number,
|
||||||
|
secure?: boolean,
|
||||||
|
sameSite?: string,
|
||||||
|
domain?: string,
|
||||||
|
httpOnly?: boolean
|
||||||
|
}
|
||||||
|
}
|
||||||
clientId?: string,
|
clientId?: string,
|
||||||
responseType?: 'token' | 'code',
|
responseType?: 'token' | 'code',
|
||||||
prompt?: '' | 'none' | 'login' | 'consent',
|
prompt?: '' | 'none' | 'login' | 'consent',
|
||||||
@@ -32,9 +50,9 @@ const defaults: ModuleOptions = {
|
|||||||
home: '/'
|
home: '/'
|
||||||
},
|
},
|
||||||
endpoints: {
|
endpoints: {
|
||||||
authorization: 'https://accounts.bitinflow.com/oauth/authorize',
|
authorization: 'https://example.com/oauth/authorize',
|
||||||
token: 'https://accounts.bitinflow.com/oauth/token',
|
token: 'https://example.com/oauth/token',
|
||||||
userInfo: 'https://accounts.bitinflow.com/api/v3/user',
|
userInfo: 'https://example.com/api/users/me',
|
||||||
logout: null,
|
logout: null,
|
||||||
},
|
},
|
||||||
refreshToken: {
|
refreshToken: {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {CookieRef, navigateTo, useCookie, useRuntimeConfig} from "#app";
|
import {CookieRef, navigateTo, useRuntimeConfig} from "#app";
|
||||||
import {ModuleOptions} from "../../module";
|
import {ModuleOptions} from "../../module";
|
||||||
import {generateRandomString, getChallengeFromVerifier} from "../support";
|
import { generateRandomString, getChallengeFromVerifier, useCookie } from '../support'
|
||||||
|
|
||||||
declare interface ComposableOptions {
|
declare interface ComposableOptions {
|
||||||
fetchUserOnInitialization: boolean
|
fetchUserOnInitialization: boolean
|
||||||
@@ -14,9 +14,9 @@ export default async (options: ComposableOptions = {
|
|||||||
fetchUserOnInitialization: false
|
fetchUserOnInitialization: false
|
||||||
}) => {
|
}) => {
|
||||||
const authConfig = useRuntimeConfig().public.oauth as ModuleOptions;
|
const authConfig = useRuntimeConfig().public.oauth as ModuleOptions;
|
||||||
if (user == null) user = useCookie('oauth_user');
|
if (user == null) user = useCookie('oauth_user', authConfig);
|
||||||
if (accessToken == null) accessToken = useCookie('oauth_access_token');
|
if (accessToken == null) accessToken = useCookie('oauth_access_token', authConfig);
|
||||||
if (refreshToken == null) refreshToken = useCookie('oauth_refresh_token');
|
if (refreshToken == null) refreshToken = useCookie('oauth_refresh_token', authConfig);
|
||||||
|
|
||||||
const fetchUser = async (): Promise<void> => {
|
const fetchUser = async (): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
@@ -36,7 +36,7 @@ export default async (options: ComposableOptions = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const signIn = async (): Promise<void> => {
|
const signIn = async (): Promise<void> => {
|
||||||
const state = useCookie<string>('oauth_state');
|
const state = useCookie<string>('oauth_state', authConfig);
|
||||||
state.value = generateRandomString();
|
state.value = generateRandomString();
|
||||||
|
|
||||||
// create oauth authorization url
|
// create oauth authorization url
|
||||||
@@ -50,7 +50,7 @@ export default async (options: ComposableOptions = {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (authConfig.responseType === 'code') {
|
if (authConfig.responseType === 'code') {
|
||||||
const codeVerifier = useCookie<string>('oauth_code_verifier');
|
const codeVerifier = useCookie<string>('oauth_code_verifier', authConfig);
|
||||||
codeVerifier.value = generateRandomString();
|
codeVerifier.value = generateRandomString();
|
||||||
|
|
||||||
params.set('code_challenge', await getChallengeFromVerifier(codeVerifier.value))
|
params.set('code_challenge', await getChallengeFromVerifier(codeVerifier.value))
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import {addRouteMiddleware, defineNuxtPlugin, navigateTo, useCookie} from '#app'
|
import {addRouteMiddleware, defineNuxtPlugin, navigateTo} from '#app'
|
||||||
import useAuth from "./composables/useAuth"
|
import useAuth from "./composables/useAuth"
|
||||||
import {RouteLocationNormalized} from "vue-router";
|
import {RouteLocationNormalized} from "vue-router";
|
||||||
import {ModuleOptions} from "../module";
|
import {ModuleOptions} from "../module";
|
||||||
|
import {useCookie} from "./support";
|
||||||
|
|
||||||
interface AccessToken {
|
interface AccessToken {
|
||||||
access_token: string,
|
access_token: string,
|
||||||
@@ -39,8 +40,9 @@ export default defineNuxtPlugin(() => {
|
|||||||
if (to.query['code']) {
|
if (to.query['code']) {
|
||||||
const code = to.query['code'] as string;
|
const code = to.query['code'] as string;
|
||||||
const stateFromRequest = to.query['state'] as string;
|
const stateFromRequest = to.query['state'] as string;
|
||||||
const stateFromCookie = useCookie<string>('oauth_state');
|
console.log('options', authConfig.cookies?.options)
|
||||||
const codeVerifier = useCookie<string>('oauth_code_verifier');
|
const stateFromCookie = useCookie<string>('oauth_state', authConfig);
|
||||||
|
const codeVerifier = useCookie<string>('oauth_code_verifier', authConfig);
|
||||||
|
|
||||||
if (stateFromRequest !== stateFromCookie.value) {
|
if (stateFromRequest !== stateFromCookie.value) {
|
||||||
console.warn('State mismatch', stateFromRequest, stateFromCookie.value)
|
console.warn('State mismatch', stateFromRequest, stateFromCookie.value)
|
||||||
|
|||||||
@@ -2,6 +2,9 @@
|
|||||||
* Source: https://docs.cotter.app/sdk-reference/api-for-other-mobile-apps/api-for-mobile-apps
|
* Source: https://docs.cotter.app/sdk-reference/api-for-other-mobile-apps/api-for-mobile-apps
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { ModuleOptions } from '../module'
|
||||||
|
import {useCookie as _useCookie, CookieRef} from "#app";
|
||||||
|
|
||||||
function dec2hex(dec: any) {
|
function dec2hex(dec: any) {
|
||||||
return ('0' + dec.toString(16)).substr(-2)
|
return ('0' + dec.toString(16)).substr(-2)
|
||||||
}
|
}
|
||||||
@@ -34,3 +37,10 @@ function base64urlencode(a: any) {
|
|||||||
export async function getChallengeFromVerifier(v: any) {
|
export async function getChallengeFromVerifier(v: any) {
|
||||||
return base64urlencode(await sha256(v));
|
return base64urlencode(await sha256(v));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useCookie<T = string | null | undefined>(name: string, options: ModuleOptions): CookieRef<T> {
|
||||||
|
const cookieName = options.cookies?.names?.[name] || name;
|
||||||
|
const cookiePrefix = options.cookies?.prefix || '';
|
||||||
|
const cookieOptions = options.cookies?.options || {};
|
||||||
|
return _useCookie(cookiePrefix + cookieName, cookieOptions)
|
||||||
|
}
|
||||||
|
|||||||
18
yarn.lock
18
yarn.lock
@@ -1680,12 +1680,12 @@ autoprefixer@^10.4.15:
|
|||||||
picocolors "^1.0.0"
|
picocolors "^1.0.0"
|
||||||
postcss-value-parser "^4.2.0"
|
postcss-value-parser "^4.2.0"
|
||||||
|
|
||||||
axios@^1.3.5:
|
axios@^1.6.0:
|
||||||
version "1.5.0"
|
version "1.7.7"
|
||||||
resolved "https://registry.yarnpkg.com/axios/-/axios-1.5.0.tgz#f02e4af823e2e46a9768cfc74691fdd0517ea267"
|
resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.7.tgz#2f554296f9892a72ac8d8e4c5b79c14a91d0a47f"
|
||||||
integrity sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==
|
integrity sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==
|
||||||
dependencies:
|
dependencies:
|
||||||
follow-redirects "^1.15.0"
|
follow-redirects "^1.15.6"
|
||||||
form-data "^4.0.0"
|
form-data "^4.0.0"
|
||||||
proxy-from-env "^1.1.0"
|
proxy-from-env "^1.1.0"
|
||||||
|
|
||||||
@@ -2809,10 +2809,10 @@ flatted@^3.2.7:
|
|||||||
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787"
|
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787"
|
||||||
integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==
|
integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==
|
||||||
|
|
||||||
follow-redirects@^1.15.0:
|
follow-redirects@^1.15.6:
|
||||||
version "1.15.2"
|
version "1.15.9"
|
||||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
|
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1"
|
||||||
integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==
|
integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==
|
||||||
|
|
||||||
form-data@^4.0.0:
|
form-data@^4.0.0:
|
||||||
version "4.0.0"
|
version "4.0.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user