@frigear-nu/oauth-server 0.0.0-stage → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -2
- package/dist/module.d.mts +206 -0
- package/dist/module.json +9 -0
- package/dist/module.mjs +107 -0
- package/dist/runtime/app/composables/index.d.ts +1 -0
- package/dist/runtime/app/composables/index.js +1 -0
- package/dist/runtime/app/composables/useOAuthApplications.d.ts +19 -0
- package/dist/runtime/app/composables/useOAuthApplications.js +41 -0
- package/dist/runtime/app/pages/admin.d.vue.ts +3 -0
- package/dist/runtime/app/pages/admin.vue +384 -0
- package/dist/runtime/app/pages/admin.vue.d.ts +3 -0
- package/dist/runtime/app/pages/applications.d.vue.ts +3 -0
- package/dist/runtime/app/pages/applications.vue +50 -0
- package/dist/runtime/app/pages/applications.vue.d.ts +3 -0
- package/dist/runtime/app/pages/authorize.d.vue.ts +3 -0
- package/dist/runtime/app/pages/authorize.vue +108 -0
- package/dist/runtime/app/pages/authorize.vue.d.ts +3 -0
- package/dist/runtime/menus.d.ts +2 -0
- package/dist/runtime/menus.js +27 -0
- package/dist/runtime/server/db/schema.sqlite.d.ts +682 -0
- package/dist/runtime/server/db/schema.sqlite.js +60 -0
- package/dist/runtime/server/routes/api/admin/application-revoke.post.d.ts +2 -0
- package/dist/runtime/server/routes/api/admin/application-revoke.post.js +7 -0
- package/dist/runtime/server/routes/api/admin/application-secret.post.d.ts +2 -0
- package/dist/runtime/server/routes/api/admin/application-secret.post.js +5 -0
- package/dist/runtime/server/routes/api/admin/application.delete.d.ts +2 -0
- package/dist/runtime/server/routes/api/admin/application.delete.js +6 -0
- package/dist/runtime/server/routes/api/admin/application.get.d.ts +2 -0
- package/dist/runtime/server/routes/api/admin/application.get.js +6 -0
- package/dist/runtime/server/routes/api/admin/application.patch.d.ts +2 -0
- package/dist/runtime/server/routes/api/admin/application.patch.js +9 -0
- package/dist/runtime/server/routes/api/admin/applications.get.d.ts +2 -0
- package/dist/runtime/server/routes/api/admin/applications.get.js +11 -0
- package/dist/runtime/server/routes/api/admin/applications.post.d.ts +2 -0
- package/dist/runtime/server/routes/api/admin/applications.post.js +8 -0
- package/dist/runtime/server/routes/api/consent.get.d.ts +2 -0
- package/dist/runtime/server/routes/api/consent.get.js +19 -0
- package/dist/runtime/server/routes/api/me-applications.get.d.ts +2 -0
- package/dist/runtime/server/routes/api/me-applications.get.js +4 -0
- package/dist/runtime/server/routes/oidc/authorize.get.d.ts +2 -0
- package/dist/runtime/server/routes/oidc/authorize.get.js +15 -0
- package/dist/runtime/server/routes/oidc/authorize.post.d.ts +2 -0
- package/dist/runtime/server/routes/oidc/authorize.post.js +27 -0
- package/dist/runtime/server/routes/oidc/jwks.get.d.ts +2 -0
- package/dist/runtime/server/routes/oidc/jwks.get.js +4 -0
- package/dist/runtime/server/routes/oidc/openid-configuration.get.d.ts +2 -0
- package/dist/runtime/server/routes/oidc/openid-configuration.get.js +21 -0
- package/dist/runtime/server/routes/oidc/revoke.post.d.ts +2 -0
- package/dist/runtime/server/routes/oidc/revoke.post.js +13 -0
- package/dist/runtime/server/routes/oidc/token.post.d.ts +2 -0
- package/dist/runtime/server/routes/oidc/token.post.js +56 -0
- package/dist/runtime/server/routes/oidc/userinfo.get.d.ts +2 -0
- package/dist/runtime/server/routes/oidc/userinfo.get.js +18 -0
- package/dist/runtime/server/utils/oauth-access.d.ts +34 -0
- package/dist/runtime/server/utils/oauth-access.js +49 -0
- package/dist/runtime/server/utils/oauth-applications.d.ts +65 -0
- package/dist/runtime/server/utils/oauth-applications.js +126 -0
- package/dist/runtime/server/utils/oauth-authorize.d.ts +29 -0
- package/dist/runtime/server/utils/oauth-authorize.js +33 -0
- package/dist/runtime/server/utils/oauth-config.d.ts +16 -0
- package/dist/runtime/server/utils/oauth-config.js +20 -0
- package/dist/runtime/server/utils/oauth-crypto.d.ts +18 -0
- package/dist/runtime/server/utils/oauth-crypto.js +48 -0
- package/dist/runtime/server/utils/oauth-grants.d.ts +60 -0
- package/dist/runtime/server/utils/oauth-grants.js +70 -0
- package/dist/runtime/server/utils/oauth-keys.d.ts +41 -0
- package/dist/runtime/server/utils/oauth-keys.js +55 -0
- package/dist/runtime/server/utils/oauth-tokens.d.ts +25 -0
- package/dist/runtime/server/utils/oauth-tokens.js +34 -0
- package/dist/runtime/utils/index.d.ts +24 -0
- package/dist/runtime/utils/index.js +51 -0
- package/dist/types.d.mts +3 -0
- package/package.json +92 -3
package/README.md
CHANGED
|
@@ -1,3 +1,50 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @frigear-nu/oauth-server
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
An OAuth 2.0 / OpenID Connect server for the frigear ecosystem. Register **applications**, restrict them to users
|
|
4
|
+
with certain roles, and let those users sign in to them through your app (authorization code flow with PKCE, rotating
|
|
5
|
+
refresh tokens, RS256 ID tokens). The module stays bare-bone: roles, the admin gate and extra claims are supplied by the
|
|
6
|
+
consuming app through Nitro hooks.
|
|
7
|
+
|
|
8
|
+
## Quick Setup
|
|
9
|
+
|
|
10
|
+
Requires `@nuxthub/core` and `@frigear-nu/users` (server mode). `@frigear-nu/menu` (account tiles) and `@nuxt/ui`
|
|
11
|
+
(the shipped pages) are optional.
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
export default defineNuxtConfig({
|
|
15
|
+
modules: ['@frigear-nu/menu', '@frigear-nu/users', '@frigear-nu/oauth-server'],
|
|
16
|
+
oauthServer: {
|
|
17
|
+
issuer: 'https://id.example.com',
|
|
18
|
+
settings: { pages: { admin: true, applications: true } },
|
|
19
|
+
},
|
|
20
|
+
})
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Create a signing key and expose it through the environment (the public key is derived from it):
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 | awk 'NF {printf "%s\\n", $0}'
|
|
27
|
+
# NUXT_OAUTH_SERVER_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Generate and commit a migration for the three `oauth_*` tables in your app (see the repository `AGENTS.md`).
|
|
31
|
+
|
|
32
|
+
Nobody can manage applications or hold a role until the host says so:
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
// server/plugins/oauth.ts
|
|
36
|
+
export default defineNitroPlugin((nitro) => {
|
|
37
|
+
nitro.hooks.hook('oauth:authorize', async (ctx) => { ctx.allowed = await isStaff(ctx.userId) })
|
|
38
|
+
nitro.hooks.hook('oauth:user:roles', async (ctx) => { ctx.roles.push(...await rolesFor(ctx.userId)) })
|
|
39
|
+
})
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Full documentation with examples: `apps/docs/content/4.modules/500.oauth-server.md`.
|
|
43
|
+
|
|
44
|
+
## Development
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pnpm --filter @frigear-nu/oauth-server test
|
|
48
|
+
pnpm --filter @frigear-nu/oauth-server lint
|
|
49
|
+
pnpm --filter @frigear-nu/oauth-server dev
|
|
50
|
+
```
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
|
+
import { H3Event } from 'h3';
|
|
3
|
+
|
|
4
|
+
type ModuleOptions = {
|
|
5
|
+
/**
|
|
6
|
+
* The `iss` claim and the discovery issuer, e.g. `https://id.example.com`. Defaults to the origin of the
|
|
7
|
+
* incoming request, which is fine in development but should be set explicitly behind proxies.
|
|
8
|
+
*/
|
|
9
|
+
issuer?: string;
|
|
10
|
+
/** `kid` of the signing key (published in the JWKS). Defaults to `key-1`; change it when rotating keys. */
|
|
11
|
+
keyId?: string;
|
|
12
|
+
/** Access and ID token lifetime in seconds. Defaults to `3600`. */
|
|
13
|
+
accessTokenTtl?: number;
|
|
14
|
+
/** Refresh token lifetime in seconds. Defaults to 30 days. */
|
|
15
|
+
refreshTokenTtl?: number;
|
|
16
|
+
/** Authorization code lifetime in seconds. Defaults to `600`. */
|
|
17
|
+
codeTtl?: number;
|
|
18
|
+
/** Path appended to an application's `websiteUrl` to form its redirect URI. Defaults to `/auth/frigear`. */
|
|
19
|
+
callbackPath?: string;
|
|
20
|
+
/** Extra scopes advertised and accepted next to `openid profile email`. Enrich their claims with `oauth:claims`. */
|
|
21
|
+
scopes?: string[];
|
|
22
|
+
settings?: {
|
|
23
|
+
pages?: {
|
|
24
|
+
/** Consent screen. On by default (`/authorize`); pass `false` to provide your own page at that path. */
|
|
25
|
+
authorize?: string | boolean;
|
|
26
|
+
/** Admin UI for applications. `true` = `/admin/oauth`. Needs `@nuxt/ui`. */
|
|
27
|
+
admin?: string | boolean;
|
|
28
|
+
/** The signed-in user's applications. `true` = `/account/applications`. Needs `@nuxt/ui`. */
|
|
29
|
+
applications?: string | boolean;
|
|
30
|
+
};
|
|
31
|
+
/** Where unauthenticated users are sent to sign in. Defaults to the `@frigear-nu/users` login page. */
|
|
32
|
+
signInPath?: string;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
/** Private runtime config (`runtimeConfig.oauthServer`). Keys are PEM strings, set them through env. */
|
|
36
|
+
type OAuthServerRuntimeConfig = {
|
|
37
|
+
/** PKCS8 RSA private key (`NUXT_OAUTH_SERVER_PRIVATE_KEY`). Single-line PEM with literal `\n` works. */
|
|
38
|
+
privateKey: string;
|
|
39
|
+
/** SPKI RSA public key (`NUXT_OAUTH_SERVER_PUBLIC_KEY`). Derived from the private key when empty. */
|
|
40
|
+
publicKey: string;
|
|
41
|
+
/**
|
|
42
|
+
* Public keys of earlier signing keys, kept during a rotation: `{ "<kid>": "<SPKI PEM>" }` (an object, or
|
|
43
|
+
* the same as a JSON string for `NUXT_OAUTH_SERVER_PREVIOUS_PUBLIC_KEYS`). They are published in the JWKS and
|
|
44
|
+
* still verify tokens, but nothing is signed with them.
|
|
45
|
+
*/
|
|
46
|
+
previousPublicKeys: string | Record<string, string>;
|
|
47
|
+
issuer?: string;
|
|
48
|
+
keyId: string;
|
|
49
|
+
accessTokenTtl: number;
|
|
50
|
+
refreshTokenTtl: number;
|
|
51
|
+
codeTtl: number;
|
|
52
|
+
callbackPath: string;
|
|
53
|
+
scopes: string[];
|
|
54
|
+
authorizePath: string;
|
|
55
|
+
signInPath?: string;
|
|
56
|
+
};
|
|
57
|
+
type PublicModuleOptions = {
|
|
58
|
+
pages: {
|
|
59
|
+
authorize?: string;
|
|
60
|
+
admin?: string;
|
|
61
|
+
applications?: string;
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
/** The user as read from the `users` table (including columns the host added through `extend.sqlite.ts`). */
|
|
65
|
+
type OAuthUser = {
|
|
66
|
+
id: number;
|
|
67
|
+
name: string;
|
|
68
|
+
email: string;
|
|
69
|
+
avatar: string | null;
|
|
70
|
+
} & Record<string, unknown>;
|
|
71
|
+
/** An application without its secret hash. */
|
|
72
|
+
type OAuthApplication = {
|
|
73
|
+
/** `app_` + random id; it is the OAuth `client_id`. */
|
|
74
|
+
id: string;
|
|
75
|
+
name: string;
|
|
76
|
+
description: string | null;
|
|
77
|
+
icon: string | null;
|
|
78
|
+
priority: number;
|
|
79
|
+
websiteUrl: string;
|
|
80
|
+
loginUrl: string | null;
|
|
81
|
+
previewUrlPattern: string | null;
|
|
82
|
+
/** Extra exact redirect URIs next to the derived `websiteUrl + callbackPath`. */
|
|
83
|
+
redirectUris: string[];
|
|
84
|
+
/** Roles allowed to sign in. Empty = every signed-in user. */
|
|
85
|
+
allowedRoles: string[];
|
|
86
|
+
tags: string[];
|
|
87
|
+
ownerId: number | null;
|
|
88
|
+
isActive: boolean;
|
|
89
|
+
createdAt: Date;
|
|
90
|
+
updatedAt: Date;
|
|
91
|
+
};
|
|
92
|
+
/** What the signed-in user needs to see to launch an application. */
|
|
93
|
+
type UserApplicationDto = Pick<OAuthApplication, 'id' | 'name' | 'description' | 'icon' | 'priority' | 'websiteUrl' | 'tags'> & {
|
|
94
|
+
loginUrl: string;
|
|
95
|
+
};
|
|
96
|
+
type ApplicationInput = {
|
|
97
|
+
name: string;
|
|
98
|
+
websiteUrl: string;
|
|
99
|
+
description?: string | null;
|
|
100
|
+
icon?: string | null;
|
|
101
|
+
priority?: number;
|
|
102
|
+
loginUrl?: string | null;
|
|
103
|
+
previewUrlPattern?: string | null;
|
|
104
|
+
redirectUris?: string[];
|
|
105
|
+
allowedRoles?: string[];
|
|
106
|
+
tags?: string[];
|
|
107
|
+
isActive?: boolean;
|
|
108
|
+
};
|
|
109
|
+
type TokenType = 'id_token' | 'access_token' | 'userinfo';
|
|
110
|
+
type AuthorizeContext = {
|
|
111
|
+
event: H3Event;
|
|
112
|
+
userId: number;
|
|
113
|
+
allowed: boolean;
|
|
114
|
+
};
|
|
115
|
+
type UserRolesContext = {
|
|
116
|
+
event?: H3Event;
|
|
117
|
+
userId: number;
|
|
118
|
+
/** Push the user's roles here. Several listeners add up. */
|
|
119
|
+
roles: string[];
|
|
120
|
+
};
|
|
121
|
+
type ApplicationAccessContext = {
|
|
122
|
+
event?: H3Event;
|
|
123
|
+
user: OAuthUser;
|
|
124
|
+
application: OAuthApplication;
|
|
125
|
+
/** The roles resolved through `oauth:user:roles`. */
|
|
126
|
+
roles: string[];
|
|
127
|
+
/** `true` when the role check passed; set `false` (and optionally `reason`) to veto. */
|
|
128
|
+
allowed: boolean;
|
|
129
|
+
reason?: string;
|
|
130
|
+
};
|
|
131
|
+
type ClaimsContext = {
|
|
132
|
+
user: OAuthUser;
|
|
133
|
+
application: OAuthApplication;
|
|
134
|
+
/** The granted scopes. */
|
|
135
|
+
scope: string[];
|
|
136
|
+
roles: string[];
|
|
137
|
+
tokenType: TokenType;
|
|
138
|
+
/** Mutate this to add, change or remove claims. Standard claims (`sub`, `iss`, `aud`, `exp`, `iat`) are set afterwards. */
|
|
139
|
+
claims: Record<string, unknown>;
|
|
140
|
+
};
|
|
141
|
+
type TokenIssuedContext = {
|
|
142
|
+
userId: number;
|
|
143
|
+
applicationId: string;
|
|
144
|
+
scope: string[];
|
|
145
|
+
grant: 'authorization_code' | 'refresh_token';
|
|
146
|
+
};
|
|
147
|
+
type TokenRevokedContext = {
|
|
148
|
+
applicationId: string;
|
|
149
|
+
userId: number | null;
|
|
150
|
+
reason: 'revoked' | 'reuse_detected' | 'user_grants_revoked';
|
|
151
|
+
};
|
|
152
|
+
/** What the admin API returns (dates are ISO strings over the wire). */
|
|
153
|
+
type AdminApplicationDto = Omit<OAuthApplication, 'createdAt' | 'updatedAt'> & {
|
|
154
|
+
createdAt: string;
|
|
155
|
+
updatedAt: string;
|
|
156
|
+
/** Refresh tokens that are still valid, i.e. users currently signed in. */
|
|
157
|
+
activeGrants?: number;
|
|
158
|
+
/** The redirect URI the application has to accept. */
|
|
159
|
+
callbackUrl: string;
|
|
160
|
+
};
|
|
161
|
+
/** Returned once when an application is created. */
|
|
162
|
+
type CreatedApplicationDto = AdminApplicationDto & {
|
|
163
|
+
clientId: string;
|
|
164
|
+
clientSecret: string;
|
|
165
|
+
};
|
|
166
|
+
type ConsentDto = {
|
|
167
|
+
application: Pick<OAuthApplication, 'id' | 'name' | 'description' | 'icon' | 'websiteUrl'>;
|
|
168
|
+
scope: string[];
|
|
169
|
+
user: {
|
|
170
|
+
name: string;
|
|
171
|
+
email: string;
|
|
172
|
+
avatar: string | null;
|
|
173
|
+
};
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
declare module '@nuxt/schema' {
|
|
177
|
+
interface NuxtConfig {
|
|
178
|
+
oauthServer?: ModuleOptions;
|
|
179
|
+
}
|
|
180
|
+
interface RuntimeConfig {
|
|
181
|
+
oauthServer: OAuthServerRuntimeConfig;
|
|
182
|
+
}
|
|
183
|
+
interface PublicRuntimeConfig {
|
|
184
|
+
oauthServer: PublicModuleOptions;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
declare module 'nitropack' {
|
|
188
|
+
interface NitroRuntimeHooks {
|
|
189
|
+
/** Admin gate (manage applications): set `ctx.allowed = true` to let the user in. Denied by default. */
|
|
190
|
+
'oauth:authorize': (ctx: AuthorizeContext) => void | Promise<void>;
|
|
191
|
+
/** Push the user's roles onto `ctx.roles`. Without a listener nobody has a role. */
|
|
192
|
+
'oauth:user:roles': (ctx: UserRolesContext) => void | Promise<void>;
|
|
193
|
+
/** Extra veto after the role check: set `ctx.allowed = false` (and `ctx.reason`) to deny. */
|
|
194
|
+
'oauth:application:access': (ctx: ApplicationAccessContext) => void | Promise<void>;
|
|
195
|
+
/** Enrich ID tokens, access tokens and userinfo: mutate `ctx.claims`. */
|
|
196
|
+
'oauth:claims': (ctx: ClaimsContext) => void | Promise<void>;
|
|
197
|
+
/** Tokens were issued (code exchange or refresh). Audit logging etc. */
|
|
198
|
+
'oauth:token:issued': (ctx: TokenIssuedContext) => void | Promise<void>;
|
|
199
|
+
/** A refresh token (family) was revoked. */
|
|
200
|
+
'oauth:token:revoked': (ctx: TokenRevokedContext) => void | Promise<void>;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
|
|
204
|
+
|
|
205
|
+
export { _default as default };
|
|
206
|
+
export type { AdminApplicationDto, ApplicationAccessContext, ApplicationInput, AuthorizeContext, ClaimsContext, ConsentDto, CreatedApplicationDto, ModuleOptions, OAuthApplication, OAuthServerRuntimeConfig, OAuthUser, PublicModuleOptions, TokenIssuedContext, TokenRevokedContext, TokenType, UserApplicationDto, UserRolesContext };
|
package/dist/module.json
ADDED
package/dist/module.mjs
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { defineNuxtModule, createResolver, hasNuxtModule, addServerHandler, addServerImportsDir, addImports, extendPages } from '@nuxt/kit';
|
|
2
|
+
import { registerModule, registerDatabaseTable, defineModulePage, moduleApiUrl } from '@frigear-nu/module-kit/utils';
|
|
3
|
+
|
|
4
|
+
const MODULE_NAME = "@frigear-nu/oauth-server";
|
|
5
|
+
const API_NAME = "oauthServer";
|
|
6
|
+
function resolvePagePath(setting, defaultPath) {
|
|
7
|
+
if (typeof setting === "string") return setting;
|
|
8
|
+
if (setting === true) return defaultPath;
|
|
9
|
+
return void 0;
|
|
10
|
+
}
|
|
11
|
+
const module$1 = defineNuxtModule({
|
|
12
|
+
meta: { name: MODULE_NAME, configKey: "oauthServer" },
|
|
13
|
+
defaults: {
|
|
14
|
+
keyId: "key-1",
|
|
15
|
+
accessTokenTtl: 3600,
|
|
16
|
+
refreshTokenTtl: 60 * 60 * 24 * 30,
|
|
17
|
+
codeTtl: 600,
|
|
18
|
+
callbackPath: "/auth/frigear",
|
|
19
|
+
scopes: [],
|
|
20
|
+
settings: { pages: {} }
|
|
21
|
+
},
|
|
22
|
+
moduleDependencies: {
|
|
23
|
+
"@nuxthub/core": { version: ">=0.10" },
|
|
24
|
+
"@frigear-nu/module-kit": { version: ">=0.0.1" },
|
|
25
|
+
"@frigear-nu/users": { version: ">=0.0.1" },
|
|
26
|
+
"@frigear-nu/menu": { version: ">=0.0.1", optional: true }
|
|
27
|
+
},
|
|
28
|
+
async setup(options, nuxt) {
|
|
29
|
+
registerModule(MODULE_NAME, nuxt);
|
|
30
|
+
const resolver = createResolver(import.meta.url);
|
|
31
|
+
const settings = options.settings ?? {};
|
|
32
|
+
const withMenus = hasNuxtModule("@frigear-nu/menu");
|
|
33
|
+
const authorizePath = settings.pages?.authorize === false ? "/authorize" : resolvePagePath(settings.pages?.authorize ?? true, "/authorize");
|
|
34
|
+
const adminPath = resolvePagePath(settings.pages?.admin, "/admin/oauth");
|
|
35
|
+
const applicationsPath = resolvePagePath(settings.pages?.applications, "/account/applications");
|
|
36
|
+
const existing = nuxt.options.runtimeConfig.oauthServer;
|
|
37
|
+
nuxt.options.runtimeConfig.oauthServer = {
|
|
38
|
+
privateKey: "",
|
|
39
|
+
publicKey: "",
|
|
40
|
+
previousPublicKeys: "",
|
|
41
|
+
...existing,
|
|
42
|
+
issuer: options.issuer,
|
|
43
|
+
keyId: options.keyId ?? "key-1",
|
|
44
|
+
accessTokenTtl: options.accessTokenTtl ?? 3600,
|
|
45
|
+
refreshTokenTtl: options.refreshTokenTtl ?? 60 * 60 * 24 * 30,
|
|
46
|
+
codeTtl: options.codeTtl ?? 600,
|
|
47
|
+
callbackPath: options.callbackPath ?? "/auth/frigear",
|
|
48
|
+
scopes: options.scopes ?? [],
|
|
49
|
+
authorizePath,
|
|
50
|
+
signInPath: settings.signInPath
|
|
51
|
+
};
|
|
52
|
+
nuxt.options.runtimeConfig.public.oauthServer = {
|
|
53
|
+
pages: {
|
|
54
|
+
authorize: settings.pages?.authorize === false ? void 0 : authorizePath,
|
|
55
|
+
admin: adminPath,
|
|
56
|
+
applications: applicationsPath
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
await registerDatabaseTable(nuxt, {
|
|
60
|
+
resolver,
|
|
61
|
+
schema: (dialect) => `./runtime/server/db/schema.${dialect}`
|
|
62
|
+
});
|
|
63
|
+
const handler = (file) => resolver.resolve(`./runtime/server/routes/${file}`);
|
|
64
|
+
addServerHandler({ route: "/.well-known/openid-configuration", method: "get", handler: handler("oidc/openid-configuration.get") });
|
|
65
|
+
addServerHandler({ route: "/.well-known/jwks.json", method: "get", handler: handler("oidc/jwks.get") });
|
|
66
|
+
addServerHandler({ route: "/oauth/authorize", method: "get", handler: handler("oidc/authorize.get") });
|
|
67
|
+
addServerHandler({ route: "/oauth/authorize", method: "post", handler: handler("oidc/authorize.post") });
|
|
68
|
+
addServerHandler({ route: "/oauth/token", method: "post", handler: handler("oidc/token.post") });
|
|
69
|
+
addServerHandler({ route: "/oauth/revoke", method: "post", handler: handler("oidc/revoke.post") });
|
|
70
|
+
addServerHandler({ route: "/oauth/userinfo", method: "get", handler: handler("oidc/userinfo.get") });
|
|
71
|
+
const api = (method, path, file) => addServerHandler({ route: moduleApiUrl(API_NAME, path), method, handler: handler(file) });
|
|
72
|
+
api("get", "consent", "api/consent.get");
|
|
73
|
+
api("get", "me/applications", "api/me-applications.get");
|
|
74
|
+
api("get", "admin/applications", "api/admin/applications.get");
|
|
75
|
+
api("post", "admin/applications", "api/admin/applications.post");
|
|
76
|
+
api("get", "admin/applications/:id", "api/admin/application.get");
|
|
77
|
+
api("patch", "admin/applications/:id", "api/admin/application.patch");
|
|
78
|
+
api("delete", "admin/applications/:id", "api/admin/application.delete");
|
|
79
|
+
api("post", "admin/applications/:id/secret", "api/admin/application-secret.post");
|
|
80
|
+
api("post", "admin/applications/:id/revoke", "api/admin/application-revoke.post");
|
|
81
|
+
addServerImportsDir(resolver.resolve("./runtime/server/utils"));
|
|
82
|
+
addImports([
|
|
83
|
+
{ name: "useOAuthApplications", from: resolver.resolve("./runtime/app/composables/useOAuthApplications") },
|
|
84
|
+
{ name: "useMyApplications", from: resolver.resolve("./runtime/app/composables/useOAuthApplications") }
|
|
85
|
+
]);
|
|
86
|
+
if (settings.pages?.authorize !== false || adminPath || applicationsPath) {
|
|
87
|
+
extendPages((pages) => {
|
|
88
|
+
const existingPaths = new Set(pages.map((page) => page.path));
|
|
89
|
+
if (settings.pages?.authorize !== false && !existingPaths.has(authorizePath)) {
|
|
90
|
+
pages.push(defineModulePage(MODULE_NAME, { name: "oauth-authorize", path: authorizePath, file: resolver.resolve("./runtime/app/pages/authorize.vue") }));
|
|
91
|
+
}
|
|
92
|
+
if (adminPath && !existingPaths.has(adminPath)) {
|
|
93
|
+
pages.push(defineModulePage(MODULE_NAME, { name: "oauth-admin", path: adminPath, file: resolver.resolve("./runtime/app/pages/admin.vue") }));
|
|
94
|
+
}
|
|
95
|
+
if (applicationsPath && !existingPaths.has(applicationsPath)) {
|
|
96
|
+
pages.push(defineModulePage(MODULE_NAME, { name: "oauth-applications", path: applicationsPath, file: resolver.resolve("./runtime/app/pages/applications.vue") }));
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
if (withMenus) {
|
|
101
|
+
const { registerMenuSource } = await import('@frigear-nu/menu/utils');
|
|
102
|
+
registerMenuSource(nuxt, { file: resolver.resolve("./runtime/menus"), menu: "dashboard.account" });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
export { module$1 as default };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useOAuthApplications, useMyApplications } from './useOAuthApplications.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useOAuthApplications, useMyApplications } from "./useOAuthApplications.js";
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ApplicationInput } from '../../../types.js';
|
|
2
|
+
/** Admin CRUD for applications. Every call needs the host's `oauth:authorize` hook to allow the user. */
|
|
3
|
+
export declare function useOAuthApplications(): {
|
|
4
|
+
applications: any;
|
|
5
|
+
status: any;
|
|
6
|
+
error: any;
|
|
7
|
+
refresh: any;
|
|
8
|
+
create: (input: ApplicationInput) => Promise<any>;
|
|
9
|
+
update: (id: string, patch: Partial<ApplicationInput>) => Promise<any>;
|
|
10
|
+
remove: (id: string) => Promise<void>;
|
|
11
|
+
rotateSecret: (id: string) => Promise<any>;
|
|
12
|
+
revokeGrants: (id: string) => Promise<void>;
|
|
13
|
+
};
|
|
14
|
+
/** The applications the signed-in user may sign in to. */
|
|
15
|
+
export declare function useMyApplications(): {
|
|
16
|
+
applications: any;
|
|
17
|
+
status: any;
|
|
18
|
+
refresh: any;
|
|
19
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const BASE = "/api/oauth-server";
|
|
2
|
+
export function useOAuthApplications() {
|
|
3
|
+
const { data, status, error, refresh } = useFetch(`${BASE}/admin/applications`, {
|
|
4
|
+
key: "oauth-server:applications",
|
|
5
|
+
default: () => []
|
|
6
|
+
});
|
|
7
|
+
async function create(input) {
|
|
8
|
+
const created = await $fetch(`${BASE}/admin/applications`, { method: "POST", body: input });
|
|
9
|
+
await refresh();
|
|
10
|
+
return created;
|
|
11
|
+
}
|
|
12
|
+
async function update(id, patch) {
|
|
13
|
+
const updated = await $fetch(`${BASE}/admin/applications/${id}`, { method: "PATCH", body: patch });
|
|
14
|
+
await refresh();
|
|
15
|
+
return updated;
|
|
16
|
+
}
|
|
17
|
+
async function remove(id) {
|
|
18
|
+
await $fetch(`${BASE}/admin/applications/${id}`, { method: "DELETE" });
|
|
19
|
+
await refresh();
|
|
20
|
+
}
|
|
21
|
+
async function rotateSecret(id) {
|
|
22
|
+
const { clientSecret } = await $fetch(`${BASE}/admin/applications/${id}/secret`, { method: "POST" });
|
|
23
|
+
await refresh();
|
|
24
|
+
return clientSecret;
|
|
25
|
+
}
|
|
26
|
+
async function revokeGrants(id) {
|
|
27
|
+
await $fetch(`${BASE}/admin/applications/${id}/revoke`, { method: "POST" });
|
|
28
|
+
await refresh();
|
|
29
|
+
}
|
|
30
|
+
return { applications: data, status, error, refresh, create, update, remove, rotateSecret, revokeGrants };
|
|
31
|
+
}
|
|
32
|
+
export function useMyApplications() {
|
|
33
|
+
const { loggedIn } = useUserSession();
|
|
34
|
+
const { data, status, refresh } = useFetch(`${BASE}/me/applications`, {
|
|
35
|
+
key: "oauth-server:mine",
|
|
36
|
+
immediate: loggedIn.value,
|
|
37
|
+
watch: false,
|
|
38
|
+
default: () => []
|
|
39
|
+
});
|
|
40
|
+
return { applications: data, status, refresh };
|
|
41
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
declare const __VLS_export: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
2
|
+
declare const _default: typeof __VLS_export;
|
|
3
|
+
export default _default;
|