mirror of
https://gitlab.com/soapbox-pub/ditto.git
synced 2025-12-06 11:29:46 +00:00
Merge branch 'revoke' into 'main'
Implement OAuth revoke endpoint See merge request soapbox-pub/ditto!551
This commit is contained in:
commit
96416747c2
3 changed files with 42 additions and 7 deletions
11
src/app.ts
11
src/app.ts
|
|
@ -51,7 +51,7 @@ import {
|
||||||
statusZapSplitsController,
|
statusZapSplitsController,
|
||||||
updateZapSplitsController,
|
updateZapSplitsController,
|
||||||
} from '@/controllers/api/ditto.ts';
|
} from '@/controllers/api/ditto.ts';
|
||||||
import { emptyArrayController, emptyObjectController, notImplementedController } from '@/controllers/api/fallback.ts';
|
import { emptyArrayController, notImplementedController } from '@/controllers/api/fallback.ts';
|
||||||
import {
|
import {
|
||||||
instanceDescriptionController,
|
instanceDescriptionController,
|
||||||
instanceV1Controller,
|
instanceV1Controller,
|
||||||
|
|
@ -61,7 +61,12 @@ import { markersController, updateMarkersController } from '@/controllers/api/ma
|
||||||
import { mediaController, updateMediaController } from '@/controllers/api/media.ts';
|
import { mediaController, updateMediaController } from '@/controllers/api/media.ts';
|
||||||
import { mutesController } from '@/controllers/api/mutes.ts';
|
import { mutesController } from '@/controllers/api/mutes.ts';
|
||||||
import { notificationController, notificationsController } from '@/controllers/api/notifications.ts';
|
import { notificationController, notificationsController } from '@/controllers/api/notifications.ts';
|
||||||
import { createTokenController, oauthAuthorizeController, oauthController } from '@/controllers/api/oauth.ts';
|
import {
|
||||||
|
createTokenController,
|
||||||
|
oauthAuthorizeController,
|
||||||
|
oauthController,
|
||||||
|
revokeTokenController,
|
||||||
|
} from '@/controllers/api/oauth.ts';
|
||||||
import {
|
import {
|
||||||
configController,
|
configController,
|
||||||
frontendConfigController,
|
frontendConfigController,
|
||||||
|
|
@ -201,7 +206,7 @@ app.get('/api/v1/apps/verify_credentials', appCredentialsController);
|
||||||
app.post('/api/v1/apps', createAppController);
|
app.post('/api/v1/apps', createAppController);
|
||||||
|
|
||||||
app.post('/oauth/token', createTokenController);
|
app.post('/oauth/token', createTokenController);
|
||||||
app.post('/oauth/revoke', emptyObjectController);
|
app.post('/oauth/revoke', revokeTokenController);
|
||||||
app.post('/oauth/authorize', oauthAuthorizeController);
|
app.post('/oauth/authorize', oauthAuthorizeController);
|
||||||
app.get('/oauth/authorize', oauthController);
|
app.get('/oauth/authorize', oauthController);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import { Context } from '@hono/hono';
|
import { Context } from '@hono/hono';
|
||||||
|
|
||||||
const emptyArrayController = (c: Context) => c.json([]);
|
const emptyArrayController = (c: Context) => c.json([]);
|
||||||
const emptyObjectController = (c: Context) => c.json({});
|
|
||||||
const notImplementedController = (c: Context) => Promise.resolve(c.json({ error: 'Not implemented' }, 404));
|
const notImplementedController = (c: Context) => Promise.resolve(c.json({ error: 'Not implemented' }, 404));
|
||||||
|
|
||||||
export { emptyArrayController, emptyObjectController, notImplementedController };
|
export { emptyArrayController, notImplementedController };
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import { Storages } from '@/storages.ts';
|
||||||
import { nostrNow } from '@/utils.ts';
|
import { nostrNow } from '@/utils.ts';
|
||||||
import { parseBody } from '@/utils/api.ts';
|
import { parseBody } from '@/utils/api.ts';
|
||||||
import { aesEncrypt } from '@/utils/aes.ts';
|
import { aesEncrypt } from '@/utils/aes.ts';
|
||||||
import { generateToken } from '@/utils/auth.ts';
|
import { generateToken, getTokenHash } from '@/utils/auth.ts';
|
||||||
|
|
||||||
const passwordGrantSchema = z.object({
|
const passwordGrantSchema = z.object({
|
||||||
grant_type: z.literal('password'),
|
grant_type: z.literal('password'),
|
||||||
|
|
@ -79,6 +79,37 @@ const createTokenController: AppController = async (c) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// This endpoint only requires the token.
|
||||||
|
// I don't think having the app credentials solves anything.
|
||||||
|
const revokeTokenSchema = z.object({
|
||||||
|
token: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mastodon OAuth token revocation.
|
||||||
|
* https://docs.joinmastodon.org/methods/oauth/#revoke
|
||||||
|
*/
|
||||||
|
const revokeTokenController: AppController = async (c) => {
|
||||||
|
const body = await parseBody(c.req.raw);
|
||||||
|
const result = revokeTokenSchema.safeParse(body);
|
||||||
|
|
||||||
|
if (!result.success) {
|
||||||
|
return c.json({ error: 'Bad request', schema: result.error }, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { token } = result.data;
|
||||||
|
|
||||||
|
const kysely = await Storages.kysely();
|
||||||
|
const tokenHash = await getTokenHash(token as `token1${string}`);
|
||||||
|
|
||||||
|
await kysely
|
||||||
|
.deleteFrom('auth_tokens')
|
||||||
|
.where('token_hash', '=', tokenHash)
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
return c.json({});
|
||||||
|
};
|
||||||
|
|
||||||
async function getToken(
|
async function getToken(
|
||||||
{ pubkey, secret, relays = [] }: { pubkey: string; secret?: string; relays?: string[] },
|
{ pubkey, secret, relays = [] }: { pubkey: string; secret?: string; relays?: string[] },
|
||||||
): Promise<`token1${string}`> {
|
): Promise<`token1${string}`> {
|
||||||
|
|
@ -229,4 +260,4 @@ function addCodeToRedirectUri(redirectUri: string, code: string, state?: string)
|
||||||
return url.toString();
|
return url.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
export { createTokenController, oauthAuthorizeController, oauthController };
|
export { createTokenController, oauthAuthorizeController, oauthController, revokeTokenController };
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue