Skip to content

Commit 1393105

Browse files
committed
gert existing consents for opey
1 parent 649c68a commit 1393105

File tree

4 files changed

+171
-36
lines changed

4 files changed

+171
-36
lines changed

server/controllers/OpeyIIController.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -303,10 +303,16 @@ export class OpeyController {
303303

304304
// Check if user already has a consent for opey
305305
// If so, return the consent id
306-
const consent = await this.obpConsentsService.getExistingConsent(session)
307-
if (consent) {
308-
console.log("Existing consent: ", consent)
306+
const consentId = await this.obpConsentsService.getExistingOpeyConsentId(session)
307+
308+
if (consentId) {
309+
console.log("Existing consent ID: ", consentId)
310+
// If we have a consent id, we can get the consent from OBP
311+
const consent = await this.obpConsentsService.getConsentByConsentId(session, consentId)
312+
309313
return response.status(200).json({consent_id: consent.consent_id});
314+
} else {
315+
console.log("No existing consent ID found")
310316
}
311317
// Either here or in this method, we should check if there is already a consent stored in the session
312318

server/services/OBPConsentsService.ts

Lines changed: 76 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,28 @@ import axios from 'axios'
77
import { Session } from 'express-session'
88

99
@Service()
10+
/**
11+
* Service for managing Open Banking Project (OBP) consents functionality.
12+
* This class handles the creation of consent clients, consent creation, and retrieval
13+
* based on user sessions.
14+
*
15+
* @class OBPConsentsService
16+
* @description Provides methods to interact with OBP Consent APIs, allowing the application
17+
* to create and manage consents that permit access to user accounts via API Explorer II.
18+
*
19+
* Key functionalities:
20+
* - Creating consent API clients based on user sessions
21+
* - Creating implicit consents for access delegation i.e. for opey
22+
* - Retrieving existing consents by ID
23+
* - Finding consents associated with specific consumers (e.g., Opey)
24+
*
25+
* @requires OBPClientService
26+
* @requires Configuration
27+
* @requires ConsentApi
28+
* @requires InlineResponse2017
29+
* @requires ConsentsIMPLICITBody1
30+
* @requires axios
31+
*/
1032
export default class OBPConsentsService {
1133
private consentApiConfig: Configuration
1234
public obpClientService: OBPClientService // This needs to be changed once we migrate away from the old OBP SDK
@@ -106,31 +128,58 @@ export default class OBPConsentsService {
106128

107129
}
108130

109-
async getExistingConsent(session: Session): Promise<any> {
131+
132+
133+
/**
134+
* Retrieves a consent by consent ID for the current user.
135+
*
136+
* This method fetches a specific consent using its ID and updates the session
137+
* with the retrieved consent data under the opeyConfig property.
138+
*
139+
* @param session - The user's session object, which must contain clientConfig with valid OAuth tokens
140+
* @param consentId - The unique identifier of the consent to retrieve
141+
* @returns Promise resolving to the consent data retrieved from OBP API
142+
* @throws Error if the user is not logged in (no valid clientConfig or accessToken)
143+
* @throws Error if the request to get the consent fails
144+
*/
145+
async getConsentByConsentId(session: Session, consentId: string): Promise<any> {
146+
147+
const clientConfig = session['clientConfig']
148+
if (!clientConfig || !clientConfig.oauthConfig.accessToken) {
149+
throw new Error('User is not logged in')
150+
}
151+
152+
try {
153+
const response = await this._sendOBPRequest(`/obp/v5.1.0/user/current/consents/${consentId}`, 'GET', clientConfig)
154+
155+
session['opeyConfig'] = {
156+
authConfig: {
157+
obpConsent: response.data
158+
}
159+
}
160+
161+
return response.data
162+
} catch (error) {
163+
console.error(error)
164+
throw new Error(`Consent with ID ${consentId} not retrieved: ${error}`)
165+
}
166+
}
167+
168+
async getExistingOpeyConsentId(session: Session): Promise<any> {
110169
// Get Consents for the current user, check if any of them are for Opey
111170
// If so, return the consent
112171

113172
// I.e. this is done by iterating and finding the consent with the correct consumer ID
114173

115174
// Get the Consents API client from the OBP SDK
116-
// The OBP SDK is fucked here, so we'll need to use Fetch until the SWAGGER WILL ACTUALLY WORK
175+
// The OBP SDK is messed up here, so we'll need to use Fetch until the SWAGGER WILL ACTUALLY WORK
117176
// const client = await this.createUserConsentsClient(session, '/obp/v5.1.0/my/consents/IMPLICIT', 'POST')
118177
// if (!client) {
119178
// throw new Error('Could not create Consents API client')
120179
// }
121180

122181

123182
// Function to send an OBP request using the logged in user's OAuth1 headers
124-
const sendOBPRequest = async (path: string, method: string, clientConfig: any) =>{
125-
const oauth1Headers = await this.obpClientService.getOAuthHeader(path, method, clientConfig)
126-
const config = {
127-
headers: {
128-
'Authorization': oauth1Headers,
129-
'Content-Type': 'application/json',
130-
}
131-
}
132-
return axios.get(`${clientConfig.baseUri}${path}`, config)
133-
}
134183

135184
const clientConfig = session['clientConfig']
136185
if (!clientConfig || !clientConfig.oauthConfig.accessToken) {
@@ -142,19 +191,19 @@ export default class OBPConsentsService {
142191

143192
let opeyConsentId: string | null = null
144193
try {
145-
const response = await sendOBPRequest(consentInfosPath, 'GET', clientConfig)
194+
const response = await this._sendOBPRequest(consentInfosPath, 'GET', clientConfig)
146195
const consents = response.data.consents
147196

148197
const opeyConsumerID = process.env.VITE_OPEY_CONSUMER_ID
149198
if (!opeyConsumerID) {
150199
throw new Error('Opey Consumer ID is missing, please set VITE_OPEY_CONSUMER_ID')
151200
}
152201

153-
202+
console.log('consents data: \n', response.data) //DEBUG
154203

155204
for (const consent of consents) {
156-
console.log('consent ', consent)
157-
if (consent.consumer_id === opeyConsumerID && consent.staus === 'ACCEPTED') {
205+
console.log(`consent_consumer_id: ${consent.consumer_id}, opey_consumer_id: ${opeyConsumerID}\n consent_status: ${consent.status}`) //DEBUG
206+
if (consent.consumer_id === opeyConsumerID && consent.status === 'ACCEPTED') {
158207
opeyConsentId = consent.consent_id
159208
break
160209
}
@@ -163,30 +212,26 @@ export default class OBPConsentsService {
163212
if (!opeyConsentId) {
164213
console.log('getExistingConsent: No consent found for Opey for current user')
165214
return null
215+
} else {
216+
return opeyConsentId
166217
}
167218

168219
} catch (error) {
169220
console.error(error)
170221
throw new Error(`Could not get existing consent info, ${error}`)
171222
}
172223

173-
// Now try to get the consent using the consent ID
174-
try {
175-
const response = await sendOBPRequest(`/obp/v5.1.0/user/current/consents/${opeyConsentId}`, 'GET', clientConfig)
176-
177-
session['opeyConfig'] = {
178-
authConfig: {
179-
obpConsent: response.data
180-
}
224+
}
225+
226+
async _sendOBPRequest (path: string, method: string, clientConfig: any) {
227+
const oauth1Headers = await this.obpClientService.getOAuthHeader(path, method, clientConfig)
228+
const config = {
229+
headers: {
230+
'Authorization': oauth1Headers,
231+
'Content-Type': 'application/json',
181232
}
182-
183-
return response.data
184-
} catch (error) {
185-
console.error(error)
186-
throw new Error(`Could not get existing consent, ${error}`)
187233
}
188-
189-
234+
return axios.get(`${clientConfig.baseUri}${path}`, config)
190235
}
191236

192237

server/services/OpeyClientService.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,10 @@ export default class OpeyClientService {
207207
}
208208
}
209209

210+
// async respondToToolApproval(tool_approval_response): Promise<ReadableStream> {
211+
212+
// }
213+
210214

211215
/**
212216
* Checks if the authentication configuration in the OpeyConfig is valid.

server/test/OBPConsentsService.test.ts

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
import {describe, it, vi, Mock } from 'vitest'
1+
import {describe, it, vi, Mock, Mocked} from 'vitest'
22
import { ConsentApi, InlineResponse2017 } from 'obp-api-typescript'
33
import { APIClientConfig, OAuthConfig } from 'obp-typescript';
4-
import { AxiosResponse } from 'axios'
4+
import axios, { AxiosResponse } from 'axios'
5+
6+
vi.mock('axios')
7+
const mockedAxios = axios as Mocked<typeof axios>;
58

69
const mockGetOAuthHeader = vi.fn(async () => (`OAuth oauth_consumer_key="jgaawf2fnj4yixqdsfaq4gipt4v1wvgsxgre",oauth_nonce="JiGDBWA3MAyKtsd9qkfWCxfju36bMjsA",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1741364123",oauth_version="1.0",oauth_signature="sa%2FRylnsdfLK8VPZI%2F2WkGFlTKs%3D"`));
710
const mockGetDirectLoginToken = vi.fn(async () => {
@@ -22,6 +25,7 @@ vi.mock('../services/OBPClientService', () => {
2225
})
2326

2427
import OBPConsentsService from '../services/OBPConsentsService';
28+
import OpeyClientService from '../services/OpeyClientService';
2529

2630
describe('OBPConsentsService.createUserConsentsClient', () => {
2731
let obpConsentsService: OBPConsentsService;
@@ -155,4 +159,80 @@ describe('OBPConsentsService.createConsent', () => {
155159
expect(mockSession.opeyConfig.authConfig.obpConsent).toHaveProperty('status', 'INITIATED');
156160
expect(mockSession.opeyConfig.authConfig.obpConsent).toHaveProperty('jwt', 'asdjfawieofaowbfaowhh2084h02pefhh0.20fh02h0h29eyf09q3h09h.2-hf4-8h284hf0h0h0284r0');
157161
});
162+
})
163+
164+
describe('OBPConsentsService.getExistingOpeyConsentId', () => {
165+
let obpConsentsService: OBPConsentsService;
166+
let mockOBPv310CreateConsentImplicit: Mock
167+
let mockConsentApi: ConsentApi;
168+
let mockSession: any;
169+
let opeyConsumerId: string;
170+
171+
172+
beforeEach(() => {
173+
vi.clearAllMocks();
174+
175+
opeyConsumerId = "06739ada-c316-488e-a8bf-1cafd8a83d98"
176+
177+
obpConsentsService = new OBPConsentsService();
178+
mockSession = {
179+
clientConfig: <APIClientConfig>{
180+
baseUri: 'https://test.openbankproject.com',
181+
version: 'v5.1.0',
182+
oauthConfig: <OAuthConfig>{
183+
consumerKey: 'jgaawf2fnj4yixqdsfaq4gipt4v1wvgsxgre',
184+
consumerSecret: 'asdofasdpfjawpefapwehhpfawheofphawfefh',
185+
accessToken: {
186+
key: 'asdhfiwah83o74gha8ygd8020ga8g28eoiahd',
187+
secret: 'hpdasf79a4hahp9h29pphphepuuhu9hwpwufhpuw9eh',
188+
}
189+
}
190+
}
191+
}
192+
})
193+
it('given a list of consents containing a matching consent_id for opey, return that consent', async () => {
194+
// Mock the axios function used in getExistingConsent
195+
mockedAxios.get.mockImplementationOnce(() => Promise.resolve(
196+
{
197+
data: {
198+
consents: [
199+
{
200+
consent_id: 'd225cfa0-7156-4bb8-a538-229dd994ed24',
201+
consumer_id: '06739ada-c316-488e-a8bf-1cafd8a83d98',
202+
created_by_user_id: '38c50591-0210-4c68-8957-8e98d6729a65',
203+
last_action_date: '2025-04-10',
204+
last_usage_date: '2025-04-10T14:13:27Z',
205+
status: 'ACCEPTED',
206+
api_standard: 'obp',
207+
api_version: ''
208+
},
209+
{
210+
consent_id: '3a5ec308-db13-460d-a497-49c1d65da439',
211+
consumer_id: '123hd131-c316-488e-a8bf-1cafd8a83d98',
212+
created_by_user_id: '38c50591-0210-4c68-8957-8e98d6729a65',
213+
last_action_date: '2025-04-10',
214+
last_usage_date: '2025-04-10T13:54:56Z',
215+
status: 'ACCEPTED',
216+
api_standard: 'obp',
217+
api_version: ''
218+
},
219+
{
220+
consent_id: '5bc2f5af-26b5-425e-8506-84c728fb5c1a',
221+
consumer_id: '06739ada-c316-488e-a8bf-1cafd8a83d98',
222+
created_by_user_id: '38c50591-0210-4c68-8957-8e98d6729a65',
223+
last_action_date: '2025-04-10',
224+
last_usage_date: '2025-04-10T13:51:55Z',
225+
status: 'INITIATED',
226+
api_standard: 'obp',
227+
api_version: ''
228+
}
229+
]
230+
}
231+
}
232+
))
233+
234+
const consentId = await obpConsentsService.getExistingOpeyConsentId(mockSession)
235+
236+
expect(consentId).toBe('d225cfa0-7156-4bb8-a538-229dd994ed24')
237+
})
158238
})

0 commit comments

Comments
 (0)