wip: oauth
This commit is contained in:
parent
0dac7b9785
commit
72b13f5265
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -4,3 +4,5 @@ dist
|
||||||
.output
|
.output
|
||||||
.nuxt
|
.nuxt
|
||||||
.env
|
.env
|
||||||
|
|
||||||
|
registered-apps.json
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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
|
||||||
|
|
41
scripts/registerApps.ts
Normal file
41
scripts/registerApps.ts
Normal 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' })
|
26
server/api/[server]/login.ts
Normal file
26
server/api/[server]/login.ts
Normal 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()
|
||||||
|
})
|
|
@ -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
Normal file
21
server/shared.ts
Normal 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]
|
||||||
|
}
|
Loading…
Reference in a new issue