wip: oauth

zio/stable
Anthony Fu 2022-11-15 22:29:46 +08:00
parent 0dac7b9785
commit 72b13f5265
7 changed files with 115 additions and 4 deletions

2
.gitignore vendored
View File

@ -4,3 +4,5 @@ dist
.output .output
.nuxt .nuxt
.env .env
registered-apps.json

View File

@ -1,5 +1,6 @@
{ {
"private": true, "private": true,
"type": "module",
"packageManager": "pnpm@7.9.0", "packageManager": "pnpm@7.9.0",
"scripts": { "scripts": {
"build": "nuxi build", "build": "nuxi build",
@ -17,11 +18,13 @@
"@iconify-json/twemoji": "^1.1.5", "@iconify-json/twemoji": "^1.1.5",
"@nuxtjs/color-mode": "^3.1.8", "@nuxtjs/color-mode": "^3.1.8",
"@pinia/nuxt": "^0.4.3", "@pinia/nuxt": "^0.4.3",
"@types/fs-extra": "^9.0.13",
"@types/sanitize-html": "^2.6.2", "@types/sanitize-html": "^2.6.2",
"@unocss/nuxt": "^0.46.5", "@unocss/nuxt": "^0.46.5",
"@vueuse/nuxt": "^9.5.0", "@vueuse/nuxt": "^9.5.0",
"eslint": "^8.27.0", "eslint": "^8.27.0",
"form-data": "^4.0.0", "form-data": "^4.0.0",
"fs-extra": "^10.1.0",
"masto": "^4.6.1", "masto": "^4.6.1",
"nuxt": "^3.0.0-rc.13", "nuxt": "^3.0.0-rc.13",
"pinia": "^2.0.23", "pinia": "^2.0.23",

View File

@ -8,11 +8,13 @@ specifiers:
'@iconify-json/twemoji': ^1.1.5 '@iconify-json/twemoji': ^1.1.5
'@nuxtjs/color-mode': ^3.1.8 '@nuxtjs/color-mode': ^3.1.8
'@pinia/nuxt': ^0.4.3 '@pinia/nuxt': ^0.4.3
'@types/fs-extra': ^9.0.13
'@types/sanitize-html': ^2.6.2 '@types/sanitize-html': ^2.6.2
'@unocss/nuxt': ^0.46.5 '@unocss/nuxt': ^0.46.5
'@vueuse/nuxt': ^9.5.0 '@vueuse/nuxt': ^9.5.0
eslint: ^8.27.0 eslint: ^8.27.0
form-data: ^4.0.0 form-data: ^4.0.0
fs-extra: ^10.1.0
masto: ^4.6.1 masto: ^4.6.1
nuxt: ^3.0.0-rc.13 nuxt: ^3.0.0-rc.13
pinia: ^2.0.23 pinia: ^2.0.23
@ -30,11 +32,13 @@ devDependencies:
'@iconify-json/twemoji': 1.1.5 '@iconify-json/twemoji': 1.1.5
'@nuxtjs/color-mode': 3.1.8 '@nuxtjs/color-mode': 3.1.8
'@pinia/nuxt': 0.4.3_typescript@4.8.4 '@pinia/nuxt': 0.4.3_typescript@4.8.4
'@types/fs-extra': 9.0.13
'@types/sanitize-html': 2.6.2 '@types/sanitize-html': 2.6.2
'@unocss/nuxt': 0.46.5 '@unocss/nuxt': 0.46.5
'@vueuse/nuxt': 9.5.0_nuxt@3.0.0-rc.13 '@vueuse/nuxt': 9.5.0_nuxt@3.0.0-rc.13
eslint: 8.27.0 eslint: 8.27.0
form-data: 4.0.0 form-data: 4.0.0
fs-extra: 10.1.0
masto: 4.6.1 masto: 4.6.1
nuxt: 3.0.0-rc.13_rmayb2veg2btbq6mbmnyivgasy nuxt: 3.0.0-rc.13_rmayb2veg2btbq6mbmnyivgasy
pinia: 2.0.23_typescript@4.8.4 pinia: 2.0.23_typescript@4.8.4
@ -1169,6 +1173,12 @@ packages:
resolution: {integrity: sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==} resolution: {integrity: sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==}
dev: true dev: true
/@types/fs-extra/9.0.13:
resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==}
dependencies:
'@types/node': 18.7.23
dev: true
/@types/json-schema/7.0.11: /@types/json-schema/7.0.11:
resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==} resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==}
dev: true dev: true

View File

@ -0,0 +1,41 @@
import fs from 'fs-extra'
import type { Client } from 'masto'
import { $fetch } from 'ohmyfetch'
import { APP_NAME } from '~~/constants'
const KNOWN_SERVERS = [
'mastodon.social',
'mas.to',
'fosstodon.org',
]
const filename = 'public/registered-apps.json'
let registeredApps: Record<string, Client> = {}
if (fs.existsSync(filename))
registeredApps = await fs.readJSON(filename)
for (const server of KNOWN_SERVERS) {
if (registeredApps[server])
continue
const app = await $fetch(`https://${server}/api/v1/apps`, {
method: 'POST',
body: {
client_name: APP_NAME,
redirect_uris: [
'urn:ietf:wg:oauth:2.0:oob',
'http://localhost:3000/*',
'https://nuxtodon.netlify.app/*',
].join('\n'),
scopes: 'read write follow push',
},
})
registeredApps[server] = app
console.log(`Registered app for ${server}`)
}
await fs.writeJSON(filename, registeredApps, { spaces: 2, EOL: '\n' })

View File

@ -0,0 +1,26 @@
import { stringifyQuery } from 'ufo'
import { getApp } from '~/server/shared'
import { HOST_DOMAIN } from '~/constants'
export default defineEventHandler(async ({ context, res }) => {
const server = context.params.server
const app = await getApp(server)
if (!app) {
res.statusCode = 400
return `App not registered for server: ${server}`
}
const query = stringifyQuery({
client_id: app.client_id,
scope: 'read write follow push',
redirect_uri: `${HOST_DOMAIN}/api/${server}/oauth`,
response_type: 'code',
})
const url = `https://${server}/oauth/authorize?${query}`
res.writeHead(302, {
Location: url,
})
res.end()
})

View File

@ -1,21 +1,29 @@
import { getQuery } from 'ufo' import { getQuery } from 'ufo'
import { getApp } from '~/server/shared'
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
const server = event.context.params.server
const app = await getApp(server)
if (!app) {
event.res.statusCode = 400
return `App not registered for server: ${server}`
}
const query = getQuery(event.req.url!) const query = getQuery(event.req.url!)
const code = query.code const code = query.code
const server = event.context.params.server
console.log({ query, server })
const res = await $fetch(`https://${server}/oauth/token`, { const res = await $fetch(`https://${server}/oauth/token`, {
method: 'POST', method: 'POST',
body: { body: {
client_id: 'your_client_id_here', client_id: app.client_id,
client_secret: 'your_client_secret_here', client_secret: app.client_secret,
redirect_uri: 'urn:ietf:wg:oauth:2.0:oob', redirect_uri: 'urn:ietf:wg:oauth:2.0:oob',
grant_type: 'authorization_code', grant_type: 'authorization_code',
code, code,
scope: 'read write follow push', scope: 'read write follow push',
}, },
}) })
console.log({ res }) console.log({ res })
}) })

21
server/shared.ts 100644
View File

@ -0,0 +1,21 @@
import { $fetch } from 'ohmyfetch'
export interface AppInfo {
id: string
name: string
website: string | null
redirect_uri: string
client_id: string
client_secret: string
vapid_key: string
}
export const registeredApps: Record<string, AppInfo> = {}
const promise = $fetch(process.env.APPS_JSON_URL || 'http://localhost:3000/registered-apps.json')
.then(r => Object.assign(registeredApps, r))
export async function getApp(server: string) {
await promise
return registeredApps[server]
}