Skip to content

Commit

Permalink
first-party client 처리
Browse files Browse the repository at this point in the history
  • Loading branch information
tirr-c committed Jul 29, 2023
1 parent bc4f448 commit 2dbeb5f
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 19 deletions.
3 changes: 3 additions & 0 deletions src/model/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import * as Bunyan from 'bunyan'
import { ControllableError } from './errors'
import Config from '../config'
import Transaction from './transaction'
import OAuth from './oauth'

const PSQL_SERIALIZATION_FAILURE = '40001'
const PSQL_DEADLOCK_DETECTED = '40P01'
Expand All @@ -22,6 +23,7 @@ export default class Model {
public readonly permissions: Permissions
public readonly shells: Shells
public readonly hosts: Hosts
public readonly oauth: OAuth

private readonly pgConfig: pg.PoolConfig
private readonly pgPool: pg.Pool
Expand All @@ -36,6 +38,7 @@ export default class Model {
this.permissions = new Permissions(this)
this.shells = new Shells(this)
this.hosts = new Hosts(this)
this.oauth = new OAuth()
}

/**
Expand Down
27 changes: 27 additions & 0 deletions src/model/oauth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// @ts-expect-error: https://github.com/microsoft/TypeScript/issues/49721
import type { AllClientMetadata } from 'oidc-provider'

import { NoSuchEntryError } from './errors'
import Transaction from './transaction'

export default class OAuth {
public async getClientById(tr: Transaction, id: string): Promise<AllClientMetadata> {
const client = await tr.query('SELECT client_id, client_secret, client_name FROM oauth_client WHERE client_id = $1', [id])
const redirectUri = await tr.query('SELECT redirect_uri FROM oauth_client_redirect_uris WHERE client_id = $1', [id])
if (client.rows.length !== 1) {
throw new NoSuchEntryError()
}

return {
client_id: client.rows[0].client_id,
client_secret: client.rows[0].client_secret,
client_name: client.rows[0].client_name,
redirect_uris: redirectUri.rows.map(row => row.redirect_uri),
}
}

public async isFirstParty(tr: Transaction, id: string): Promise<boolean> {
const result = await tr.query('SELECT first_party FROM oauth_client WHERE client_id = $1', [id])
return Boolean(result.rows[0]?.first_party)
}
}
17 changes: 2 additions & 15 deletions src/oidc/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Redis from 'ioredis'
import type { Adapter, AdapterPayload } from 'oidc-provider'

import Model from '../model/model'
import {NoSuchEntryError} from '../model/errors'
import { NoSuchEntryError } from '../model/errors'

const grantable = new Set([
'AccessToken',
Expand Down Expand Up @@ -86,20 +86,7 @@ export default function AdapterFactory(redisUrl: string, model: Model) {
async find(id: string): Promise<AdapterPayload | undefined | void> {
if (this.name === 'Client') {
try {
return model.pgDo(async tr => {
const client = await tr.query('select client_id, client_secret, client_name from oauth_client where client_id = $1', [id])
const redirectUri = await tr.query('select redirect_uri from oauth_client_redirect_uris where client_id = $1', [id])
if (client.rows.length !== 1) {
throw new NoSuchEntryError()
}

return {
client_id: client.rows[0].client_id,
client_secret: client.rows[0].client_secret,
client_name: client.rows[0].client_name,
redirect_uris: redirectUri.rows.map(row => row.redirect_uri),
}
})
return model.pgDo(tr => model.oauth.getClientById(tr, id))
} catch (e) {
if (e instanceof NoSuchEntryError) {
return undefined
Expand Down
50 changes: 46 additions & 4 deletions src/oidc/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import Config from '../config'
import OIDCAccount from './account'
import AdapterFactory from './adapter'

const claims = {
openid: ['sub', 'groups', 'username'],
};

export default function createOIDCConfig(model: Model, oidcConfig: Config['oidc']): Configuration {
let adapter
if (oidcConfig.redisURL) {
Expand All @@ -14,7 +18,6 @@ export default function createOIDCConfig(model: Model, oidcConfig: Config['oidc'
return {
adapter,
findAccount: async (ctx, id) => {

const [groups, username] = await model.pgDo(async tr => {
const groupSet = await model.users.getUserReachableGroups(tr, Number(id))
const groupResult = await tr.query('SELECT identifier FROM groups WHERE idx = ANY($1)', [[...groupSet]])
Expand All @@ -31,6 +34,47 @@ export default function createOIDCConfig(model: Model, oidcConfig: Config['oidc'

return new OIDCAccount(id, username, groups)
},
async loadExistingGrant(ctx) {
if (!ctx.oidc.client || !ctx.oidc.session || !ctx.oidc.result) {
return undefined
}

const clientId = ctx.oidc.client.clientId
const grantId = ctx.oidc.result.consent?.grantId
|| ctx.oidc.session.grantIdFor(ctx.oidc.client.clientId)

if (grantId) {
// keep grant expiry aligned with session expiry
// to prevent consent prompt being requested when grant expires
const grant = await ctx.oidc.provider.Grant.find(grantId)

// this aligns the Grant ttl with that of the current session
// if the same Grant is used for multiple sessions, or is set
// to never expire, you probably do not want this in your code
if (grant && ctx.oidc.account && (!grant.exp || grant.exp < ctx.oidc.session.exp)) {
grant.exp = ctx.oidc.session.exp

await grant.save()
}

return grant
} else {
const isFirstParty = await model.pgDo(tr => model.oauth.isFirstParty(tr, clientId))
if (isFirstParty) {
const grant = new ctx.oidc.provider.Grant({
clientId: clientId,
accountId: ctx.oidc.session.accountId,
})

grant.addOIDCScope('openid')
grant.addOIDCClaims(claims.openid)
await grant.save()
return grant
}
}

return undefined
},
cookies: {
keys: [oidcConfig.cookieKey]
},
Expand All @@ -44,9 +88,7 @@ export default function createOIDCConfig(model: Model, oidcConfig: Config['oidc'
return `/oauth/${interaction.uid}`
},
},
claims: {
openid: ['sub', 'groups', 'username'],
},
claims,
features: {
devInteractions: {
enabled: oidcConfig.devInteractions
Expand Down

0 comments on commit 2dbeb5f

Please sign in to comment.