Skip to content

Commit

Permalink
feat(identity): use forward identity urn if present
Browse files Browse the repository at this point in the history
  • Loading branch information
szkl committed Feb 29, 2024
1 parent 48d9e0a commit a774831
Show file tree
Hide file tree
Showing 11 changed files with 107 additions and 24 deletions.
2 changes: 1 addition & 1 deletion apps/passport/app/routes/authorize.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ export const loader: LoaderFunction = getRollupReqFunctionErrorWrapper(
const responseType = ResponseType.Code
const preauthorizeRes =
await coreClient.authorization.preauthorize.mutate({
identity: identityURN,
identityURN,
responseType,
clientId,
redirectUri,
Expand Down
13 changes: 11 additions & 2 deletions packages/platform-middleware/jwt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import { IdentityURNSpace, type IdentityURN } from '@proofzero/urns/identity'
import { getAuthzTokenFromReq } from '@proofzero/utils'
import { checkToken } from '@proofzero/utils/token'

import { type Environment } from '@proofzero/platform.core/src/types'
import { initIdentityNodeByName } from '@proofzero/platform.identity/src/nodes'

import { BaseMiddlewareFunction } from './types'

export const AuthorizationTokenFromHeader: BaseMiddlewareFunction<{
Expand All @@ -20,15 +23,21 @@ export const AuthorizationTokenFromHeader: BaseMiddlewareFunction<{
}

export const ValidateJWT: BaseMiddlewareFunction<{
env: Environment
token?: string
}> = ({ ctx, next }) => {
}> = async ({ ctx, next }) => {
if (ctx.token) {
const { sub: subject } = checkToken(ctx.token)
if (subject && IdentityURNSpace.is(subject)) {
const identityNode = initIdentityNodeByName(subject, ctx.env.Identity)
const forwardIdentityURN =
await identityNode.class.getForwardIdentityURN()
const identityURN = forwardIdentityURN || subject

return next({
ctx: {
...ctx,
identityURN: subject,
identityURN,
},
})
}
Expand Down
13 changes: 11 additions & 2 deletions platform/account/src/jsonrpc/methods/deleteAccountNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { RollupError, ERROR_CODES } from '@proofzero/errors'
import { IdentityURNSpace } from '@proofzero/urns/identity'
import { AccountURN } from '@proofzero/urns/account'

import { initIdentityNodeByName } from '@proofzero/platform.identity/src/nodes'
import { GetEdgesMethodOutput } from '@proofzero/platform.edges/src/jsonrpc/methods/getEdges'

import type { Context } from '../../context'
Expand All @@ -28,12 +29,20 @@ export const deleteAccountNodeMethod = async ({
input: DeleteAccountNodeParams
ctx: Context
}) => {
const { identityURN, forceDelete } = input
const { forceDelete } = input

if (!IdentityURNSpace.is(identityURN)) throw new Error('Invalid identity URN')
if (!IdentityURNSpace.is(input.identityURN))
throw new Error('Invalid identity URN')
if (!ctx.accountURN) throw new Error('missing account URN')
if (!ctx.account) throw new Error('missing account node')

const identityNode = initIdentityNodeByName(
input.identityURN,
ctx.env.Identity
)
const forwardIdentityURN = await identityNode.class.getForwardIdentityURN()
const identityURN = forwardIdentityURN || input.identityURN

const caller = router.createCaller(ctx)

if (!forceDelete) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { IdentityURNSpace } from '@proofzero/urns/identity'
import { appRouter } from '../router'
import { getClaimValues } from '@proofzero/security/persona'

import { initIdentityNodeByName } from '@proofzero/platform.identity/src/nodes'

export const GetAuthorizedAppScopesMethodInput = z.object({
identityURN: inputValidators.IdentityURNInput,
clientId: z.string().min(1),
Expand Down Expand Up @@ -47,7 +49,14 @@ export const getAuthorizedAppScopesMethod = async ({
input: GetAuthorizedAppScopesMethodParams
ctx: Context
}): Promise<GetAuthorizedAppScopesMethodResult> => {
const { identityURN, clientId } = input
const { clientId } = input

const identityNode = initIdentityNodeByName(
input.identityURN,
ctx.env.Identity
)
const forwardIdentityURN = await identityNode.class.getForwardIdentityURN()
const identityURN = forwardIdentityURN || input.identityURN

const nss = `${IdentityURNSpace.decode(identityURN)}@${clientId}`
const urn = AuthorizationURNSpace.componentizedUrn(nss)
Expand Down
13 changes: 11 additions & 2 deletions platform/authorization/src/jsonrpc/methods/getPersonaData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { BadRequestError } from '@proofzero/errors'
import { AuthorizationURNSpace } from '@proofzero/urns/authorization'
import { IdentityURNSpace } from '@proofzero/urns/identity'

import { initIdentityNodeByName } from '@proofzero/platform.identity/src/nodes'

import { Context } from '../../context'
import { initAuthorizationNodeByName } from '../../nodes'
import { PersonaData } from '@proofzero/types/application'
Expand All @@ -22,18 +24,25 @@ export const getPersonaDataMethod = async ({
input: z.infer<typeof GetPersonaDataInput>
ctx: Context
}): Promise<z.infer<typeof GetPersonaDataOutput>> => {
const { identityURN, clientId } = input
const { clientId } = input

if (!clientId)
throw new BadRequestError({
message: 'missing client id',
})

if (!IdentityURNSpace.is(identityURN))
if (!IdentityURNSpace.is(input.identityURN))
throw new BadRequestError({
message: 'missing identity',
})

const identityNode = initIdentityNodeByName(
input.identityURN,
ctx.env.Identity
)
const forwardIdentityURN = await identityNode.class.getForwardIdentityURN()
const identityURN = forwardIdentityURN || input.identityURN

const nss = `${IdentityURNSpace.decode(identityURN)}@${clientId}`
const urn = AuthorizationURNSpace.componentizedUrn(nss)
const authorizationNode = initAuthorizationNodeByName(
Expand Down
15 changes: 12 additions & 3 deletions platform/authorization/src/jsonrpc/methods/preauthorize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ import { appRouter } from '../router'
import { AuthorizationURNSpace } from '@proofzero/urns/authorization'
import { IdentityURNSpace } from '@proofzero/urns/identity'

import { initIdentityNodeByName } from '@proofzero/platform.identity/src/nodes'

export const PreAuthorizeMethodInput = z.object({
identity: IdentityURNInput,
identityURN: IdentityURNInput,
responseType: z.string(),
clientId: z.string(),
redirectUri: z.string(),
Expand Down Expand Up @@ -42,9 +44,16 @@ export const preauthorizeMethod = async ({
}): Promise<PreAuthorizeOutputParams> => {
let preauthorized = false

const { identity, clientId, scope: requestedScope } = input
const { clientId, scope: requestedScope } = input

const identityNode = initIdentityNodeByName(
input.identityURN,
ctx.env.Identity
)
const forwardIdentityURN = await identityNode.class.getForwardIdentityURN()
const identityURN = forwardIdentityURN || input.identityURN

const nss = `${IdentityURNSpace.decode(identity)}@${clientId}`
const nss = `${IdentityURNSpace.decode(identityURN)}@${clientId}`
const urn = AuthorizationURNSpace.componentizedUrn(nss)
const authorizationNode = initAuthorizationNodeByName(
urn,
Expand Down
8 changes: 7 additions & 1 deletion platform/authorization/src/jsonrpc/methods/revokeToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import type { AuthorizationJWTPayload } from '@proofzero/types/authorization'
import { AuthorizationURNSpace } from '@proofzero/urns/authorization'
import { IdentityURNSpace } from '@proofzero/urns/identity'

import { initIdentityNodeByName } from '@proofzero/platform.identity/src/nodes'

import { Context } from '../../context'
import { getJWKS } from '../../jwk'
import { initAuthorizationNodeByName } from '../../nodes'
Expand Down Expand Up @@ -51,7 +53,11 @@ export const revokeTokenMethod: RevokeTokenMethod = async ({ ctx, input }) => {
if (clientId != payload.aud[0]) throw MismatchClientIdError
if (!payload.sub) throw MissingSubjectError

const identityURN = payload.sub
let identityURN = payload.sub
const identityNode = initIdentityNodeByName(identityURN, ctx.env.Identity)
const forwardIdentityURN = await identityNode.class.getForwardIdentityURN()
if (forwardIdentityURN) identityURN = forwardIdentityURN

const nss = `${IdentityURNSpace.decode(identityURN)}@${clientId}`
const urn = AuthorizationURNSpace.componentizedUrn(nss)
const node = initAuthorizationNodeByName(urn, ctx.env.Authorization)
Expand Down
8 changes: 7 additions & 1 deletion platform/authorization/src/jsonrpc/methods/verifyToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { AuthorizationURNSpace } from '@proofzero/urns/authorization'
import { IdentityURNSpace } from '@proofzero/urns/identity'
import { getErrorCause } from '@proofzero/utils/errors'

import { initIdentityNodeByName } from '@proofzero/platform.identity/src/nodes'

import { Context } from '../../context'
import { getJWKS } from '../../jwk'
import { initAuthorizationNodeByName } from '../../nodes'
Expand Down Expand Up @@ -58,7 +60,11 @@ export const verifyTokenMethod: VerifyTokenMethod = async ({ ctx, input }) => {
if (clientId && clientId != payload.aud[0]) throw MismatchClientIdError
if (!payload.sub) throw MissingSubjectError

const identityURN = payload.sub
let identityURN = payload.sub
const identityNode = initIdentityNodeByName(identityURN, ctx.env.Identity)
const forwardIdentityURN = await identityNode.class.getForwardIdentityURN()
if (forwardIdentityURN) identityURN = forwardIdentityURN

const nss = `${IdentityURNSpace.decode(identityURN)}@${clientId}`
const urn = AuthorizationURNSpace.componentizedUrn(nss)
const node = initAuthorizationNodeByName(urn, ctx.env.Authorization)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { IdentityURNSpace } from '@proofzero/urns/identity'
import { AuthorizationJWTPayload } from '@proofzero/types/authorization'
import { BaseMiddlewareFunction } from '@proofzero/platform-middleware/types'

import { initIdentityNodeByName } from '@proofzero/platform.identity/src/nodes'

import { initAuthorizationNodeByName } from '../../nodes'
import { Context } from '../../context'

Expand All @@ -17,17 +19,20 @@ export const setAuthorizationNode: BaseMiddlewareFunction<Context> = async ({
if (!ctx.token) throw new Error('No token found in middleware context')

const jwt = decodeJwt(ctx.token) as AuthorizationJWTPayload
const identityURN = jwt.sub
const [clientId] = jwt.aud

if (!clientId) {
throw new Error('missing client id in the aud claim')
}

if (!IdentityURNSpace.is(identityURN)) {
if (!IdentityURNSpace.is(jwt.sub)) {
throw new Error(`missing identity in the sub claim`)
}

const identityNode = initIdentityNodeByName(jwt.sub, ctx.env.Identity)
const forwardIdentityURN = await identityNode.class.getForwardIdentityURN()
const identityURN = forwardIdentityURN || jwt.sub

const nss = `${IdentityURNSpace.decode(identityURN)}@${clientId}`
const urn = AuthorizationURNSpace.componentizedUrn(nss)
const authorizationNode = initAuthorizationNodeByName(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import { router } from '@proofzero/platform.core'
import { EDGE_MEMBER_OF_IDENTITY_GROUP } from '@proofzero/types/graph'

import { Context } from '../../../context'
import { initIdentityGroupNodeByName } from '../../../nodes'
import {
initIdentityNodeByName,
initIdentityGroupNodeByName,
} from '../../../nodes'

export const HasIdentityGroupPermissionsInputSchema = z.object({
identityURN: IdentityURNInput,
Expand All @@ -34,10 +37,17 @@ export const hasIdentityGroupPermissions = async ({
}): Promise<HasIdentityGroupPermissionsOutput> => {
const caller = router.createCaller(ctx)

const identityNode = initIdentityNodeByName(
input.identityURN,
ctx.env.Identity
)
const forwardIdentityURN = await identityNode.class.getForwardIdentityURN()
const identityURN = forwardIdentityURN || input.identityURN

const { edges } = await caller.edges.getEdges({
query: {
src: {
baseUrn: input.identityURN,
baseUrn: identityURN,
},
tag: EDGE_MEMBER_OF_IDENTITY_GROUP,
dst: {
Expand All @@ -50,7 +60,7 @@ export const hasIdentityGroupPermissions = async ({
input.identityGroupURN,
ctx.env.IdentityGroup
)
const { error } = await DO.class.validateAdmin(input.identityURN)
const { error } = await DO.class.validateAdmin(identityURN)

return {
read: edges.length > 0,
Expand Down
23 changes: 17 additions & 6 deletions platform/identity/src/nodes/identity-group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ import {
import { IdentityURN } from '@proofzero/urns/identity'
import { DOProxy } from 'do-proxy'
import { NodeMethodReturnValue } from '@proofzero/types/node'

import { Environment } from '@proofzero/platform.core'

import { initIdentityNodeByName } from '.'
import { IDENTITY_GROUP_OPTIONS } from '../constants'

export type InviteMemberInput = {
Expand All @@ -36,10 +40,12 @@ export type ClearInvitationInput = {

export default class IdentityGroup extends DOProxy {
declare state: DurableObjectState
declare env: Environment

constructor(state: DurableObjectState) {
super(state)
constructor(state: DurableObjectState, env: Environment) {
super(state, env)
this.state = state
this.env = env
}

async getServicePlans(): Promise<ServicePlans | undefined> {
Expand Down Expand Up @@ -189,11 +195,16 @@ export default class IdentityGroup extends DOProxy {
}

async getOrderedMembers(): Promise<IdentityURN[]> {
const orderedMembers = await this.state.storage.get<IdentityURN[]>(
'orderedMembers'
const orderedMembers =
(await this.state.storage.get<IdentityURN[]>('orderedMembers')) || []

return Promise.all(
orderedMembers.map(async (urn) => {
const node = initIdentityNodeByName(urn, this.env.Identity)
const forwardURN = await node.class.getForwardIdentityURN()
return forwardURN || urn
})
)

return orderedMembers || []
}

async setOrderedMembers(members: IdentityURN[]): Promise<void> {
Expand Down

0 comments on commit a774831

Please sign in to comment.