Skip to content

Commit 9cf9df6

Browse files
authored
feat: upgrade rafiki (#2157)
* feat: upgrade rafiki * fix: change schema urls * fix: resolve fixture issue * fix: update open payments url * fix: remove/add tenant id on requests as needed
1 parent 9d4ac6e commit 9cf9df6

14 files changed

Lines changed: 368 additions & 80 deletions

File tree

docker/dev/.env.example

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,6 @@ CARD_DATA_HREF=
2828
PRIVATE_KEY=
2929
KEY_ID=
3030
PAYMENT_POINTER=
31+
32+
OPERATOR_TENANT_ID=
33+
ADMIN_API_SECRET=

docker/dev/docker-compose.yml

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ services:
7474
STRIPE_SECRET_KEY: ${STRIPE_SECRET_KEY}
7575
STRIPE_WEBHOOK_SECRET: ${STRIPE_WEBHOOK_SECRET}
7676
USE_STRIPE: ${USE_STRIPE}
77+
OPERATOR_TENANT_ID: ${OPERATOR_TENANT_ID:-f829c064-762a-4430-ac5d-7af5df198551}
78+
ADMIN_API_SECRET: ${ADMIN_API_SECRET:-secret-key}
79+
ADMIN_SIGNATURE_VERSION: 1
7780
restart: always
7881
networks:
7982
- testnet
@@ -114,7 +117,7 @@ services:
114117
# Rafiki
115118
rafiki-auth:
116119
container_name: rafiki-auth
117-
image: ghcr.io/interledger/rafiki-auth:v1.2.0-beta
120+
image: ghcr.io/interledger/rafiki-auth:v2.0.0-beta
118121
restart: always
119122
networks:
120123
- testnet
@@ -134,13 +137,16 @@ services:
134137
INTERACTION_COOKIE_SAME_SITE: ${AUTH_INTERACTION_COOKIE_SAME_SITE:-lax}
135138
WAIT_SECONDS: 1
136139
REDIS_URL: redis://redis:6379/0
140+
OPERATOR_TENANT_ID: ${OPERATOR_TENANT_ID:-f829c064-762a-4430-ac5d-7af5df198551}
141+
ADMIN_API_SECRET: ${ADMIN_API_SECRET:-secret-key}
142+
ADMIN_SIGNATURE_VERSION: 1
137143
depends_on:
138144
- postgres
139145
<<: *logging
140146

141147
rafiki-backend:
142148
container_name: rafiki-backend
143-
image: ghcr.io/interledger/rafiki-backend:v1.2.0-beta
149+
image: ghcr.io/interledger/rafiki-backend:v2.0.0-beta
144150
restart: always
145151
privileged: true
146152
volumes:
@@ -169,7 +175,7 @@ services:
169175
ILP_CONNECTOR_URL: http://127.0.0.1:3002
170176
STREAM_SECRET: BjPXtnd00G2mRQwP/8ZpwyZASOch5sUXT5o0iR5b5wU=
171177
ADMIN_KEY: admin
172-
OPEN_PAYMENTS_URL: http://rafiki-backend
178+
OPEN_PAYMENTS_URL: https://rafiki-backend
173179
REDIS_URL: redis://redis:6379/0
174180
WALLET_ADDRESS_URL: https://rafiki-backend/.well-known/pay
175181
# Testnet urls - not implemented
@@ -183,14 +189,18 @@ services:
183189
SLIPPAGE: 0.01
184190
KEY_ID: rafiki
185191
WALLET_ADDRESS_REDIRECT_HTML_PAGE: ${WALLET_ADDRESS_REDIRECT_HTML_PAGE}
192+
OPERATOR_TENANT_ID: ${OPERATOR_TENANT_ID:-f829c064-762a-4430-ac5d-7af5df198551}
193+
ADMIN_API_SECRET: ${ADMIN_API_SECRET:-secret-key}
194+
ADMIN_SIGNATURE_VERSION: 1
195+
AUTH_SERVICE_API_URL: http://rafiki-auth:3011
186196
depends_on:
187197
- postgres
188198
- redis
189199
<<: *logging
190200

191201
rafiki-frontend:
192202
container_name: rafiki-frontend
193-
image: ghcr.io/interledger/rafiki-frontend:v1.2.0-beta
203+
image: ghcr.io/interledger/rafiki-frontend:v2.0.0-beta
194204
depends_on:
195205
- rafiki-backend
196206
restart: always
@@ -208,6 +218,7 @@ services:
208218
KRATOS_BROWSER_PUBLIC_URL: 'http://localhost:4433'
209219
KRATOS_ADMIN_URL: 'http://kratos:4434/admin'
210220
AUTH_ENABLED: false
221+
SIGNATURE_VERSION: 1
211222
<<: *logging
212223

213224
kratos:

packages/boutique/backend/src/open-payments/service.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,10 +167,13 @@ export class OpenPayments implements IOpenPayments {
167167
interactRef
168168
})
169169

170+
const customerWalletAddress = await this.getWalletAddress(
171+
order.payments.walletAddress
172+
)
170173
await this.opClient.outgoingPayment
171174
.create(
172175
{
173-
url: new URL(order.payments.walletAddress).origin,
176+
url: customerWalletAddress.resourceServer,
174177
accessToken: continuation.accessToken
175178
},
176179
{
@@ -220,6 +223,9 @@ export class OpenPayments implements IOpenPayments {
220223
url = url.replace('rafiki-auth', 'localhost')
221224
}
222225

226+
// remove tenant id as it is not used for hash
227+
url = url.replace(/\/[0-9a-fA-F-]+\/?$/, '')
228+
223229
const data = `${clientNonce}\n${interactNonce}\n${interactRef}\n${url}/`
224230
const hash = createHash('sha-256').update(data).digest('base64')
225231

packages/wallet/backend/codegen.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ config:
99
generates:
1010
src/rafiki/backend/generated/graphql.ts:
1111
documents: 'src/rafiki/backend/**/!(*.d).{ts,tsx}'
12-
schema: http://localhost:3011/graphql
12+
schema: https://raw.githubusercontent.com/interledger/rafiki/refs/tags/v2.0.0-beta/packages/backend/src/graphql/schema.graphql
1313
plugins:
1414
- 'typescript'
1515
- 'typescript-operations'
1616
src/rafiki/auth/generated/graphql.ts:
1717
documents: 'src/rafiki/auth/**/!(*.d).{ts,tsx}'
18-
schema: http://localhost:3008/graphql
18+
schema: https://raw.githubusercontent.com/interledger/rafiki/refs/tags/v2.0.0-beta/packages/auth/src/graphql/schema.graphql
1919
plugins:
2020
- 'typescript'
2121
- 'typescript-operations'

packages/wallet/backend/src/config/env.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ const envSchema = z.object({
3939
AUTH_DOMAIN: z.string().url().default('http://rafiki-auth:3009'),
4040
AUTH_IDENTITY_SERVER_SECRET: z.string().default('replace-me'),
4141
RAFIKI_WEBHOOK_SIGNATURE_SECRET: z.string().default('replace-me'),
42+
ADMIN_SIGNATURE_VERSION: z.string().default('1'),
43+
ADMIN_API_SECRET: z.string().default('replace-me'),
44+
OPERATOR_TENANT_ID: z.string().default('tenant'),
4245
OPEN_PAYMENTS_HOST: z.string().url().default('https://backend:80'),
4346
RAFIKI_MONEY_FRONTEND_HOST: z.string().default('localhost'),
4447
SENDGRID_API_KEY: z.string().default('SG.API_KEY'),
Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,74 @@
11
import { Env } from '@/config/env'
2-
import { GraphQLClient } from 'graphql-request'
2+
import {
3+
GraphQLClient,
4+
type RequestMiddleware,
5+
type Variables
6+
} from 'graphql-request'
7+
import { canonicalize } from 'json-canonicalize'
8+
import { createHmac } from 'crypto'
9+
10+
// Type for the request body in GraphQL requests
11+
interface GraphQLRequestBody {
12+
query: string
13+
variables?: Variables
14+
operationName?: string
15+
}
16+
17+
function createSignedClient(endpoint: string, env: Env) {
18+
const middleware: RequestMiddleware = async (request) => {
19+
try {
20+
const timestamp = Date.now()
21+
const version = env.ADMIN_SIGNATURE_VERSION
22+
23+
// Extract query from the request body
24+
let query = ''
25+
if (request.body && typeof request.body === 'string') {
26+
try {
27+
const body: GraphQLRequestBody = JSON.parse(request.body)
28+
query = body.query || ''
29+
} catch (e) {
30+
// Body is not valid JSON - this shouldn't happen with GraphQL requests
31+
// but we'll handle it gracefully
32+
query = ''
33+
}
34+
}
35+
36+
const formattedRequest = {
37+
variables: request.variables ?? {},
38+
operationName: request.operationName,
39+
query
40+
}
41+
42+
const payload = `${timestamp}.${canonicalize(formattedRequest)}`
43+
44+
const hmac = createHmac('sha256', env.ADMIN_API_SECRET)
45+
hmac.update(payload)
46+
const digest = hmac.digest('hex')
47+
48+
const signature = `t=${timestamp}, v${version}=${digest}`
49+
50+
return {
51+
...request,
52+
headers: {
53+
...(request.headers as Record<string, string>),
54+
signature,
55+
'tenant-id': env.OPERATOR_TENANT_ID
56+
}
57+
}
58+
} catch (e) {
59+
return request
60+
}
61+
}
62+
63+
return new GraphQLClient(endpoint, {
64+
requestMiddleware: middleware
65+
})
66+
}
367

468
export function createBackendGraphQLClient(env: Env) {
5-
return new GraphQLClient(env.GRAPHQL_ENDPOINT)
69+
return createSignedClient(env.GRAPHQL_ENDPOINT, env)
670
}
771

872
export function createAuthGraphQLClient(env: Env) {
9-
return new GraphQLClient(env.AUTH_GRAPHQL_ENDPOINT)
73+
return createSignedClient(env.AUTH_GRAPHQL_ENDPOINT, env)
1074
}

packages/wallet/backend/src/incomingPayment/service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ export class IncomingPaymentService implements IIncomingPaymentService {
111111

112112
return {
113113
description: receiver.metadata.description,
114-
value: parseFloat(transformAmount(value, asset.scale)),
114+
value: parseFloat(transformAmount(value.toString(), asset.scale)),
115115
assetCode: asset.code
116116
}
117117
}

packages/wallet/backend/src/rafiki/auth/generated/graphql.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ export type Scalars = {
1212
Boolean: { input: boolean; output: boolean; }
1313
Int: { input: number; output: number; }
1414
Float: { input: number; output: number; }
15-
BigInt: { input: bigint; output: bigint; }
15+
/** The `UInt8` scalar type represents unsigned 8-bit whole numeric values, ranging from 0 to 255. */
1616
UInt8: { input: number; output: number; }
17+
/** The `UInt64` scalar type represents unsigned 64-bit whole numeric values. It is capable of handling values that are larger than the JavaScript `Number` type limit (greater than 2^53). */
18+
UInt64: { input: any; output: any; }
1719
};
1820

1921
export type Access = Model & {
@@ -65,6 +67,8 @@ export type Grant = Model & {
6567
id: Scalars['ID']['output'];
6668
/** Current state of the grant. */
6769
state: GrantState;
70+
/** Unique identifier of the tenant associated with the grant. */
71+
tenantId: Scalars['ID']['output'];
6872
};
6973

7074
export type GrantEdge = {
@@ -161,7 +165,7 @@ export type PaymentAmount = {
161165
/** Difference in orders of magnitude between the standard unit of an asset and a corresponding fractional unit. */
162166
assetScale: Scalars['UInt8']['output'];
163167
/** The value of the payment amount. */
164-
value: Scalars['BigInt']['output'];
168+
value: Scalars['UInt64']['output'];
165169
};
166170

167171
export type Query = {
@@ -185,6 +189,7 @@ export type QueryGrantsArgs = {
185189
first?: InputMaybe<Scalars['Int']['input']>;
186190
last?: InputMaybe<Scalars['Int']['input']>;
187191
sortOrder?: InputMaybe<SortOrder>;
192+
tenantId?: InputMaybe<Scalars['ID']['input']>;
188193
};
189194

190195
export type RevokeGrantInput = {
@@ -214,14 +219,14 @@ export type GetGrantsQueryVariables = Exact<{
214219
}>;
215220

216221

217-
export type GetGrantsQuery = { __typename?: 'Query', grants: { __typename?: 'GrantsConnection', edges: Array<{ __typename?: 'GrantEdge', cursor: string, node: { __typename?: 'Grant', id: string, client: string, state: GrantState, finalizationReason?: GrantFinalization | null, createdAt: string, access: Array<{ __typename?: 'Access', id: string, identifier?: string | null, createdAt: string, actions: Array<string | null>, type: string, limits?: { __typename?: 'LimitData', receiver?: string | null, interval?: string | null, debitAmount?: { __typename?: 'PaymentAmount', value: bigint, assetCode: string, assetScale: number } | null, receiveAmount?: { __typename?: 'PaymentAmount', value: bigint, assetCode: string, assetScale: number } | null } | null }> } }>, pageInfo: { __typename?: 'PageInfo', endCursor?: string | null, hasNextPage: boolean, hasPreviousPage: boolean, startCursor?: string | null } } };
222+
export type GetGrantsQuery = { __typename?: 'Query', grants: { __typename?: 'GrantsConnection', edges: Array<{ __typename?: 'GrantEdge', cursor: string, node: { __typename?: 'Grant', id: string, client: string, state: GrantState, finalizationReason?: GrantFinalization | null, createdAt: string, access: Array<{ __typename?: 'Access', id: string, identifier?: string | null, createdAt: string, actions: Array<string | null>, type: string, limits?: { __typename?: 'LimitData', receiver?: string | null, interval?: string | null, debitAmount?: { __typename?: 'PaymentAmount', value: any, assetCode: string, assetScale: number } | null, receiveAmount?: { __typename?: 'PaymentAmount', value: any, assetCode: string, assetScale: number } | null } | null }> } }>, pageInfo: { __typename?: 'PageInfo', endCursor?: string | null, hasNextPage: boolean, hasPreviousPage: boolean, startCursor?: string | null } } };
218223

219224
export type GetGrantQueryVariables = Exact<{
220225
grantId: Scalars['ID']['input'];
221226
}>;
222227

223228

224-
export type GetGrantQuery = { __typename?: 'Query', grant: { __typename?: 'Grant', id: string, client: string, state: GrantState, finalizationReason?: GrantFinalization | null, createdAt: string, access: Array<{ __typename?: 'Access', id: string, identifier?: string | null, createdAt: string, actions: Array<string | null>, type: string, limits?: { __typename?: 'LimitData', receiver?: string | null, interval?: string | null, debitAmount?: { __typename?: 'PaymentAmount', value: bigint, assetCode: string, assetScale: number } | null, receiveAmount?: { __typename?: 'PaymentAmount', value: bigint, assetCode: string, assetScale: number } | null } | null }> } };
229+
export type GetGrantQuery = { __typename?: 'Query', grant: { __typename?: 'Grant', id: string, client: string, state: GrantState, finalizationReason?: GrantFinalization | null, createdAt: string, access: Array<{ __typename?: 'Access', id: string, identifier?: string | null, createdAt: string, actions: Array<string | null>, type: string, limits?: { __typename?: 'LimitData', receiver?: string | null, interval?: string | null, debitAmount?: { __typename?: 'PaymentAmount', value: any, assetCode: string, assetScale: number } | null, receiveAmount?: { __typename?: 'PaymentAmount', value: any, assetCode: string, assetScale: number } | null } | null }> } };
225230

226231
export type RevokeGrantMutationVariables = Exact<{
227232
grantId: Scalars['String']['input'];

packages/wallet/backend/src/rafiki/auth/service.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export class RafikiAuthService implements IRafikiAuthService {
3838
GetGrantsQueryVariables
3939
>(getGrantsQuery, { filter: { identifier: { in: identifiers } } })
4040

41-
return response.grants.edges.map((el: { node: Grant }) => el.node)
41+
return response.grants.edges.map((el) => el.node as Grant)
4242
}
4343

4444
async listGrantsWithPagination(args: GetGrantsQueryVariables) {
@@ -67,7 +67,7 @@ export class RafikiAuthService implements IRafikiAuthService {
6767
GetGrantQueryVariables
6868
>(getGrantByIdQuery, { grantId })
6969

70-
return response.grant
70+
return response.grant as Grant
7171
}
7272

7373
async getGrantByInteraction(interactionId: string, nonce: string) {

0 commit comments

Comments
 (0)