diff --git a/src/lib/i18n/en/oauth2.json b/src/lib/i18n/en/oauth2.json
index 85e6955..fec6a4f 100644
--- a/src/lib/i18n/en/oauth2.json
+++ b/src/lib/i18n/en/oauth2.json
@@ -2,15 +2,22 @@
"authorize": {
"title": "Authorize application",
"errorPage": "The authorization URL provided is invalid or malformed. Please forward this message to the developers of the application you came here from:",
- "authorize": "Authorize",
+ "authorize": "Authorize the application",
"reject": "Reject",
+ "allowed": "This application will have access to..",
+ "disallowed": "This application will NOT have access to..",
"scope": {
- "profile": "Username and display name",
- "email": "Email address",
- "picture": "Profile picture",
- "account": "Password and other account settings",
+ "profile": "Your username and display name",
+ "email": "Your email address",
+ "picture": "Your profile picture",
+ "account": "Changing your password or other account settings",
"management": "Manage Icy Network on your behalf",
"admin": "Commit administrative actions to the extent of your user privileges"
+ },
+ "link": {
+ "website": "Website",
+ "privacy": "Privacy policy",
+ "terms": "Terms of Service"
}
}
}
diff --git a/src/lib/server/oauth2/controller/bearer.ts b/src/lib/server/oauth2/controller/bearer.ts
new file mode 100644
index 0000000..4a243ae
--- /dev/null
+++ b/src/lib/server/oauth2/controller/bearer.ts
@@ -0,0 +1,47 @@
+import { AccessDenied } from '../error';
+import { OAuth2AccessTokens, type OAuth2AccessToken } from '../model';
+
+export class OAuth2BearerController {
+ static bearerFromRequest = async (
+ request: Request,
+ url: URL
+ ): Promise
=> {
+ let token = null;
+
+ // Look for token in header
+ if (request.headers.has('authorization')) {
+ const pieces = (request.headers.get('authorization') as string).split(' ', 2);
+
+ // Check authorization header
+ if (!pieces || pieces.length !== 2) {
+ throw new AccessDenied('Wrong authorization header');
+ }
+
+ // Only bearer auth is supported
+ if (pieces[0].toLowerCase() !== 'bearer') {
+ throw new AccessDenied('Unsupported authorization method in header');
+ }
+
+ token = pieces[1];
+ } else if (request.headers.has('x-access-token')) {
+ token = request.headers.get('x-access-token');
+ } else if (url?.searchParams.has('access_token')) {
+ token = url.searchParams.get('access_token') as string;
+ } else {
+ const body = await request.json().catch(() => ({}));
+ if (!body.access_token) {
+ throw new AccessDenied('Bearer token not found');
+ }
+ token = body.access_token;
+ }
+
+ // Try to fetch access token
+ const object = await OAuth2AccessTokens.fetchByToken(token);
+ if (!object) {
+ throw new AccessDenied('Token not found or has expired');
+ } else if (!OAuth2AccessTokens.checkTTL(object)) {
+ throw new AccessDenied('Token is expired');
+ }
+ return object;
+ };
+}
diff --git a/src/lib/server/oauth2/controller/index.ts b/src/lib/server/oauth2/controller/index.ts
index 4a69554..fb80845 100644
--- a/src/lib/server/oauth2/controller/index.ts
+++ b/src/lib/server/oauth2/controller/index.ts
@@ -1,3 +1,4 @@
export * from './authorization';
export * from './introspection';
export * from './token';
+export * from './bearer';
diff --git a/src/lib/server/oauth2/controller/tokens/authorizationCode.ts b/src/lib/server/oauth2/controller/tokens/authorizationCode.ts
index 3d3cfe8..703d5f5 100644
--- a/src/lib/server/oauth2/controller/tokens/authorizationCode.ts
+++ b/src/lib/server/oauth2/controller/tokens/authorizationCode.ts
@@ -15,9 +15,9 @@ import type { OAuth2TokenResponse } from '../../response';
/**
* Issue an access token by authorization code
- * @param oauth2 - OAuth2 instance
* @param client - OAuth2 client
* @param providedCode - Authorization code
+ * @param codeVerifier - PCKE check verifier
* @returns Access token.
*/
export async function authorizationCode(
diff --git a/src/lib/server/oauth2/controller/tokens/clientCredentials.ts b/src/lib/server/oauth2/controller/tokens/clientCredentials.ts
index fc2003e..67b27c3 100644
--- a/src/lib/server/oauth2/controller/tokens/clientCredentials.ts
+++ b/src/lib/server/oauth2/controller/tokens/clientCredentials.ts
@@ -5,7 +5,6 @@ import type { OAuth2TokenResponse } from '../../response';
/**
* Issue client access token
- * @param oauth2 - OAuth2 instance
* @param client - Client
* @param wantScope - Requested scopes
* @returns Access token
diff --git a/src/lib/server/oauth2/controller/tokens/refreshToken.ts b/src/lib/server/oauth2/controller/tokens/refreshToken.ts
index 3b89955..73e7913 100644
--- a/src/lib/server/oauth2/controller/tokens/refreshToken.ts
+++ b/src/lib/server/oauth2/controller/tokens/refreshToken.ts
@@ -12,7 +12,6 @@ import type { OAuth2TokenResponse } from '../../response';
/**
* Get a new access token from a refresh token. Scope change may not be requested.
- * @param oauth2 - OAuth2 instance
* @param client - OAuth2 client
* @param providedToken - Refresh token
* @returns Access token
diff --git a/src/lib/server/oauth2/model/tokens.ts b/src/lib/server/oauth2/model/tokens.ts
index 29320a1..0cc2d63 100644
--- a/src/lib/server/oauth2/model/tokens.ts
+++ b/src/lib/server/oauth2/model/tokens.ts
@@ -10,7 +10,6 @@ import { and, eq, sql } from 'drizzle-orm';
import { OAuth2Clients } from './client';
import { Users } from '$lib/server/users';
import { CryptoUtils } from '$lib/server/crypto-utils';
-import { AccessDenied } from '../error';
export type CodeChallengeMethod = 'plain' | 'S256';
@@ -254,46 +253,6 @@ export class OAuth2AccessTokens {
clientIdPub: find.o_auth2_client.client_id
};
}
-
- static async getFromRequest(request: Request, url?: URL): Promise {
- let token = null;
-
- // Look for token in header
- if (request.headers.has('authorization')) {
- const pieces = (request.headers.get('authorization') as string).split(' ', 2);
-
- // Check authorization header
- if (!pieces || pieces.length !== 2) {
- throw new AccessDenied('Wrong authorization header');
- }
-
- // Only bearer auth is supported
- if (pieces[0].toLowerCase() !== 'bearer') {
- throw new AccessDenied('Unsupported authorization method in header');
- }
-
- token = pieces[1];
- } else if (request.headers.has('x-access-token')) {
- token = request.headers.get('x-access-token');
- } else if (url?.searchParams.has('access_token')) {
- token = url.searchParams.get('access_token') as string;
- } else {
- const body = await request.json().catch(() => ({}));
- if (!body.access_token) {
- throw new AccessDenied('Bearer token not found');
- }
- token = body.access_token;
- }
-
- // Try to fetch access token
- const object = await OAuth2AccessTokens.fetchByToken(token);
- if (!object) {
- throw new AccessDenied('Token not found or has expired');
- } else if (!OAuth2AccessTokens.checkTTL(object)) {
- throw new AccessDenied('Token is expired');
- }
- return object;
- }
}
export class OAuth2RefreshTokens {
diff --git a/src/routes/account/+page.svelte b/src/routes/account/+page.svelte
index 9f9e33b..c85de51 100644
--- a/src/routes/account/+page.svelte
+++ b/src/routes/account/+page.svelte
@@ -155,7 +155,7 @@
{$t('account.avatar.title')}
-
+
{
} else {
// From OAuth2 bearer token
try {
- const token = await OAuth2AccessTokens.getFromRequest(request, url);
+ const token = await OAuth2BearerController.bearerFromRequest(request, url);
if (token?.userId) {
tokenScopes = OAuth2Clients.splitScope(token.scope || '');
user = await Users.getById(token.userId);
diff --git a/src/routes/oauth2/authorize/+page.svelte b/src/routes/oauth2/authorize/+page.svelte
index e4f11af..b69dce5 100644
--- a/src/routes/oauth2/authorize/+page.svelte
+++ b/src/routes/oauth2/authorize/+page.svelte
@@ -1,5 +1,5 @@
- {$t('common.siteName')}
-
{#if data.error}
+ {$t('common.siteName')}
{$t('oauth2.authorize.errorPage')}
{$t('oauth2.authorize.title')}
- {#if data.user}
-
-
- {data.user.name}
- @{data.user.username}
-
-
- {/if}
- {data.client.title}
- {data.client.description}
+ {$t('common.siteName')}
+ {$t('oauth2.authorize.title')}
-
- {#each data.client.allowedScopes as scope}
- {$t(`oauth2.authorize.scope.${scope}`)}
- {/each}
+
+ {#if data.user}
+
+
+
+ {data.user.name}
+ @{data.user.username}
+
+
+
+ {/if}
+
+
+
+
+
{data.client.title}
+
{data.client.description}
+
+
+
+
+
-
- {#each data.client.disallowedScopes as scope}
- {$t(`oauth2.authorize.scope.${scope}`)}
- {/each}
+
+ {#if data.client.allowedScopes?.length}
+
{$t('oauth2.authorize.allowed')}
+
+ {#each data.client.allowedScopes as scope}
+ {$t(`oauth2.authorize.scope.${scope}`)}
+ {/each}
+
+ {/if}
+
+ {#if data.client.disallowedScopes?.length}
+
{$t('oauth2.authorize.disallowed')}
+
+ {#each data.client.disallowedScopes as scope}
+ {$t(`oauth2.authorize.scope.${scope}`)}
+ {/each}
+
+ {/if}
-
+
+
-
+
+
{/if}
+
+