Skip to content
Snippets Groups Projects
Verified Commit 6d067dd7 authored by Louis's avatar Louis :fire:
Browse files

Add endpoints for managing oauth client redirects, fix model methods for refresh token flow

parent fa1f11e9
No related branches found
No related tags found
No related merge requests found
......@@ -19,6 +19,19 @@ class AccessToken extends BaseModel {
}
}
async toOAuthInterface() {
const client = await this.getOAuthClient()
const user = await this.getUser()
return {
accessToken: this.token,
accessTokenExpiresAt: this.expires_at,
scope: this.scope,
client: client.toOAuthInterface(),
user,
}
}
toJSON() {
const user = this.user ? {user: this.user} : {}
return {
......
......@@ -19,6 +19,19 @@ class RefreshToken extends BaseModel {
}
}
async toOAuthInterface() {
const client = await this.getOAuthClient()
const user = await this.getUser()
return {
refreshToken: this.token,
refreshTokenExpiresAt: this.expires_at,
scope: this.scope,
client: client.toOAuthInterface(),
user,
}
}
toJSON() {
const user = this.user ? { user: this.user } : { }
return {
......
......@@ -109,13 +109,14 @@ class User extends BaseModel {
return await crypto.encrypt(JSON.stringify({ session: this.id }))
}
async asJWTToken() {
async asJWTToken(extras = {}) {
const { sign } = require('core/utils/jwt')
return await sign({
session: {
id: this.id,
roles: ['overseer', 'user'],
},
...extras,
})
}
......
......@@ -15,18 +15,10 @@ function createTokenPair(user, client, access, refresh = {}) {
}
}
async function createClientEncryptedToken(client, user, scope) {
const payload = {
client_id: client.id,
user_id: user.id,
scope: scope,
}
if (client.secret == null) {
return crypto.encrypt(payload)
}
return await crypto.encryptWith(Buffer.from(client.secret, 'hex'), JSON.stringify(payload))
async function createClientEncryptedToken(client, userModel, scope) {
const user = await User.findByPk(userModel.id)
const payload = user.asJWTToken({ cid: client.id, scope })
return payload
}
const model = {
......@@ -50,8 +42,9 @@ const model = {
getAccessToken(token) {
return AccessToken.findOne({ include: [ { model: User }, { model: OAuthClient } ], where: { token: { [Op.eq]: token } } })
},
getRefreshToken(token) {
return RefreshToken.findOne({ include: [ { model: User }, { model: OAuthClient } ], where: { token: { [Op.eq]: token } } })
async getRefreshToken(token) {
const t = await RefreshToken.findOne({ include: [ { model: User }, { model: OAuthClient } ], where: { token: { [Op.eq]: token } } })
return await t?.toOAuthInterface()
},
getUser: async function getAuthUser(email, password) {
......@@ -188,6 +181,8 @@ class KoaOAuthServer {
}
}
console.log(ctx.request.query, query)
const authState = await crypto.encrypt(JSON.stringify({ redirect: 'authorize', query: ctx.request.query }))
if (!user) {
return ctx.redirect(`/login?login_state=${ authState }`)
......@@ -259,7 +254,7 @@ const scopeDescriptionMap = {
description: 'Full access to your account, including the ability to create, update and delete any user information, metrics and files.'
}
}
function describeScopeRequest(scope) {
function describeScopeRequest(scope = '*') {
const scopes = scope.split(' ')
return scopes.map(s => scopeDescriptionMap[s])
}
......
......@@ -24,4 +24,55 @@ exports.listClients = async ctx => {
ctx.body = {
clients: clients.map(c => c.toOAuthInterface())
}
}
exports.addClientRedirect = async ctx => {
const client = ctx.models?.oauthClient
const user = await ctx.services['core.auth'].getUser()
if (client?.owner_id !== user.id) {
throw new HttpError(403, 'You do not have permission to modify this oauth client')
}
const uri = ctx.request.body?.uri
if (!uri?.trim()) {
throw new HttpError(400, 'Must provide a URI to add to the client')
}
const uris = new Set(client.redirect_uris)
if (!uris.has(uri)) {
client.redirect_uris = [
...client.redirect_uris,
uri,
]
await client.save()
}
ctx.body = { client: client.toOAuthInterface() }
}
exports.removeClientRedirect = async ctx => {
const client = ctx.models?.oauthClient
const user = await ctx.services['core.auth'].getUser()
if (client.owner_id !== user.id) {
throw new HttpError(403, 'You do not have permission to modify this oauth client')
}
const uri = ctx.request.body?.uri
if (!uri?.trim()) {
throw new HttpError(400, 'Must provide a URI to add to the client')
}
const uris = new Set(client.redirect_uris)
if (uris.has(uri)) {
uris.delete(uri)
client.redirect_uris = Array.from(uris)
await client.save()
}
ctx.body = { client: client.toOAuthInterface() }
}
\ No newline at end of file
......@@ -43,6 +43,18 @@ module.exports = async (ctx, next) => {
} finally {
t.setName(`[${ ctx.method }] ${ ctx._matchedRouteName ?? ctx._matchedRoute ?? ctx.path }`)
t.setHttpStatus(ctx.status)
threadContext.stopTransaction()
const user = ctx.services['core.auth']._user
Sentry.configureScope(scope => {
if (user) {
scope.setUser({
name: user.name,
email: user.email,
id: user.id,
ip_address: ctx.ip,
})
}
threadContext.stopTransaction()
})
}
}
\ No newline at end of file
const { OAuthClient } = require('database/models')
module.exports = async (id, ctx, next) => {
ctx.models = ctx.models ?? {}
ctx.models.oauthClient = await OAuthClient.findByPk(id)
return await next()
}
\ No newline at end of file
const controller = (name, method) => require(`./controllers/${ name }`)[method]
const param = (name) => require(`./params/${ name }`)
const AuthServer = require('domain/auth/AuthServer')
const { env, config } = require('bootstrap')
......@@ -103,8 +105,12 @@ function mount(api) {
api.post('/auth/reset-token', controller('api/auth', 'triggerPasswordReset'))
api.post('/auth/reset-password', controller('api/auth', 'handlePasswordReset'))
api.param('oauthClientId', param('oauth_client'))
api.get('/oauth/clients', controller('api/oauth', 'listClients'))
api.post('/oauth/clients', controller('api/oauth', 'createClient'))
api.post('/oauth/clients/:oauthClientId/redirects', controller('api/oauth', 'addClientRedirect'))
api.delete('/oauth/clients/:oauthClientId/redirects', controller('api/oauth', 'removeClientRedirect'))
api.get('/self', controller('api/user', 'self'))
api.get('/self/bundles', controller('api/app', 'getBundles'))
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment