Skip to content

Commit 825a27c

Browse files
authored
Merge pull request #494 from NaucMeIT/customer-magic-auth
2 parents f773d9d + 437a585 commit 825a27c

File tree

5 files changed

+82
-71
lines changed

5 files changed

+82
-71
lines changed

libs/api/auth/src/api-auth.ts

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { formatApiErrorResponse } from '@nmit-coursition/api/utils'
22
import {
3+
AUTH_BRJ_COOKIES_NAME,
34
AUTH_COOKIES_NAME,
4-
getUserProfile,
5+
createBrjMagicAuth,
6+
getBrjIdentity,
57
invalidateSession,
6-
storeUserSession,
7-
updateUser,
8-
validateSessionToken,
8+
logoutBrj,
99
} from '@nmit-coursition/auth'
1010
import { secretsEnv } from '@nmit-coursition/env'
1111
import { WorkOS } from '@workos-inc/node'
@@ -32,7 +32,6 @@ export const apiAuth = new Elysia({ prefix: '/auth', tags: ['auth'] })
3232
return redirect(authorizationUrl)
3333
})
3434
.get('/callback', async ({ request, query, error, redirect, cookie }) => {
35-
const organisationId = 1 // TODO
3635
const code = String(query['code'] || '')
3736

3837
if (!code) throw error(400, formatApiErrorResponse(request, 'No code provided.'))
@@ -48,7 +47,7 @@ export const apiAuth = new Elysia({ prefix: '/auth', tags: ['auth'] })
4847
})
4948

5049
const { user, sealedSession } = authenticateResponse
51-
const internalUser = await updateUser(user, organisationId)
50+
const brjIdentity = await createBrjMagicAuth(user)
5251

5352
cookie[AUTH_COOKIES_NAME]?.set({
5453
value: sealedSession || '',
@@ -58,7 +57,15 @@ export const apiAuth = new Elysia({ prefix: '/auth', tags: ['auth'] })
5857
sameSite: 'lax',
5958
})
6059

61-
await storeUserSession(internalUser.id, sealedSession || '')
60+
if (brjIdentity) {
61+
cookie[AUTH_BRJ_COOKIES_NAME]?.set({
62+
value: brjIdentity,
63+
path: '/',
64+
httpOnly: true,
65+
secure: true,
66+
sameSite: 'lax',
67+
})
68+
}
6269

6370
return redirect('/')
6471
} catch (error) {
@@ -69,7 +76,8 @@ export const apiAuth = new Elysia({ prefix: '/auth', tags: ['auth'] })
6976
})
7077
.get('/logout', async ({ redirect, cookie }) => {
7178
const sessionData = cookie[AUTH_COOKIES_NAME]?.toString()
72-
if (!sessionData) return redirect('/login')
79+
const brjSessionData = cookie[AUTH_BRJ_COOKIES_NAME]?.toString()
80+
if (!sessionData || !brjSessionData) return redirect('/auth/login')
7381

7482
try {
7583
const session = workos.userManagement.loadSealedSession({
@@ -78,20 +86,20 @@ export const apiAuth = new Elysia({ prefix: '/auth', tags: ['auth'] })
7886
})
7987

8088
const url = await session.getLogoutUrl()
81-
cookie[AUTH_COOKIES_NAME]?.remove()
8289
await invalidateSession(sessionData)
90+
await logoutBrj(brjSessionData)
91+
cookie[AUTH_COOKIES_NAME]?.remove()
92+
cookie[AUTH_BRJ_COOKIES_NAME]?.remove()
8393

8494
return redirect(url)
8595
} catch (error) {
8696
// eslint-disable-next-line no-console debug info
8797
console.error(error)
88-
return redirect('/login')
98+
return redirect('/auth/login')
8999
}
90100
})
91-
.get('/profile', async ({ headers, cookie }) => {
92-
const session = cookie[AUTH_COOKIES_NAME]?.toString() || ''
93-
const apiKeyRaw = headers['authorization'] || ''
94-
const apiKey = apiKeyRaw || (await validateSessionToken(session))
101+
.get('/profile', async ({ cookie }) => {
102+
const brjSessionData = cookie[AUTH_BRJ_COOKIES_NAME]?.toString() || ''
95103

96-
return getUserProfile(apiKey)
104+
return getBrjIdentity(brjSessionData)
97105
})

libs/api/utils/src/lib/api-utils.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,21 @@ function initSentry(request: ExtendedRequest) {
7070
Sentry.setTag('url', request.url || 'CLI')
7171
}
7272

73-
export function reportUsage(apiKey: string, duration: number, type: 'video' | 'document' | 'web') {
74-
// eslint-disable-next-line no-console -- will be replaced with real usage reporting
75-
console.log(`API Key ${apiKey} used ${duration} on ${type}.`)
73+
export async function reportUsage(identityId: string, duration: number, type: 'video' | 'document' | 'web') {
74+
const apiKey = process.env['BRJ_API_KEY']
75+
const amount = Math.ceil(duration)
76+
await fetch(`https://brj.app/api/v1/customer/credit-spend?apiKey=${apiKey}`, {
77+
method: 'POST',
78+
headers: {
79+
'Content-Type': 'application/json',
80+
},
81+
body: JSON.stringify({
82+
apiKey,
83+
identityId,
84+
amount,
85+
description: type,
86+
}),
87+
})
7688
}
7789

7890
export async function getLoggedUserOrThrow(request: Request): Promise<cas__user> {

libs/api/v1/src/lib/api-v1.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { unlink } from 'node:fs/promises'
22
import FirecrawlApp from '@mendable/firecrawl-js'
33
import { generateQuiz, getResult, getTranscript, uploadFile, waitUntilJobIsDone } from '@nmit-coursition/ai'
44
import { apiCommonGuard, downloadPublicMedia, formatApiErrorResponse, reportUsage } from '@nmit-coursition/api/utils'
5+
import { AUTH_BRJ_COOKIES_NAME } from '@nmit-coursition/auth'
56
import { secretsEnv } from '@nmit-coursition/env'
67
import {
78
allowedDeepgramLanguagesAsType,
@@ -53,10 +54,11 @@ export const apiV1 = new Elysia({ prefix: '/v1', tags: ['v1'] })
5354
duration: t.Optional(t.Number()),
5455
}),
5556
},
56-
afterResponse({ response, headers }) {
57+
afterResponse({ response, cookie }) {
5758
if (!response || !('duration' in response) || !response.duration) return
5859
const { duration } = response
59-
duration >= 0 && reportUsage(headers['authorization'] || '', duration, 'video')
60+
const brjSessionData = cookie[AUTH_BRJ_COOKIES_NAME]?.toString() || ''
61+
duration >= 0 && reportUsage(brjSessionData, duration, 'video')
6062
},
6163
},
6264
)
@@ -107,10 +109,11 @@ export const apiV1 = new Elysia({ prefix: '/v1', tags: ['v1'] })
107109
duration: t.Optional(t.Number()),
108110
}),
109111
},
110-
afterResponse({ response, headers }) {
112+
afterResponse({ response, cookie }) {
111113
if (!response || !('duration' in response) || !response.duration) return
112114
const { duration } = response
113-
duration >= 0 && reportUsage(headers['authorization'] || '', duration, 'video')
115+
const brjSessionData = cookie[AUTH_BRJ_COOKIES_NAME]?.toString() || ''
116+
duration >= 0 && reportUsage(brjSessionData, duration, 'video')
114117
},
115118
},
116119
)
@@ -147,10 +150,11 @@ export const apiV1 = new Elysia({ prefix: '/v1', tags: ['v1'] })
147150
credits: t.Number(),
148151
}),
149152
},
150-
afterResponse({ response, headers }) {
153+
afterResponse({ response, cookie }) {
151154
if (!response || !('credits' in response) || !response.credits) return
152155
const { credits } = response
153-
credits >= 0 && reportUsage(headers['authorization'] || '', credits, 'document')
156+
const brjSessionData = cookie[AUTH_BRJ_COOKIES_NAME]?.toString() || ''
157+
credits >= 0 && reportUsage(brjSessionData, credits, 'document')
154158
},
155159
},
156160
)
@@ -189,10 +193,11 @@ export const apiV1 = new Elysia({ prefix: '/v1', tags: ['v1'] })
189193
credits: t.Number(),
190194
}),
191195
},
192-
afterResponse({ response, headers }) {
196+
afterResponse({ response, cookie }) {
193197
if (!response || !('credits' in response) || !response.credits) return
194198
const { credits } = response
195-
credits >= 0 && reportUsage(headers['authorization'] || '', credits, 'web')
199+
const brjSessionData = cookie[AUTH_BRJ_COOKIES_NAME]?.toString() || ''
200+
credits >= 0 && reportUsage(brjSessionData, credits, 'web')
196201
},
197202
},
198203
),

libs/auth/src/constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
export const AUTH_COOKIES_NAME = 'wos-session'
2+
3+
export const AUTH_BRJ_COOKIES_NAME = 'brj-identity'

libs/auth/src/user.ts

Lines changed: 29 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { prisma } from '@nmit-coursition/db'
22
import { randomStringGenerator } from '@nmit-coursition/utils'
3-
import type { cas__user } from '@prisma/client'
43
import type { User, UserProfileRawRecord, UserProfileResponse } from './typescript'
54

65
export async function getUserProfile(apiKey: string): Promise<UserProfileResponse> {
@@ -81,56 +80,41 @@ export async function createApiKey(userId: bigint): Promise<string> {
8180
return apiKey.api_key
8281
}
8382

84-
export async function updateUser(userData: User, organisationId: number): Promise<cas__user> {
85-
const user =
86-
(await prisma.cas__user.findFirst({
87-
where: {
88-
AND: [{ OR: [{ workos_id: userData.id }, { email: userData.email }] }, { organisation_id: organisationId }],
89-
},
90-
})) ||
91-
(await prisma.cas__user.create({
92-
data: {
93-
organisation_id: organisationId,
94-
email: userData.email,
95-
inserted_date: new Date(userData.createdAt),
96-
updated_date: new Date(userData.updatedAt),
97-
synced_date: new Date(),
98-
workos_id: userData.id,
99-
credit: 0,
100-
lifetime_subscribe: false,
101-
},
102-
}))
103-
104-
return prisma.cas__user.update({
105-
where: { id: user.id },
106-
data: {
107-
workos_id: userData.id,
108-
email: userData.email,
109-
inserted_date: new Date(userData.createdAt),
110-
updated_date: new Date(userData.updatedAt),
111-
synced_date: new Date(),
112-
first_name: userData.firstName || null,
113-
last_name: userData.lastName || null,
114-
avatar_url: userData.profilePictureUrl || null,
83+
export async function createBrjMagicAuth(userData: User): Promise<string> {
84+
const request = await fetch(`https://brj.app/api/v1/customer/magic-auth?apiKey=${process.env['BRJ_API_KEY']}`, {
85+
method: 'POST',
86+
headers: {
87+
'Content-Type': 'application/json',
11588
},
89+
body: JSON.stringify({
90+
apiKey: process.env['BRJ_API_KEY'],
91+
email: userData.email,
92+
firstName: userData.firstName,
93+
lastName: userData.lastName,
94+
}),
11695
})
96+
97+
const response = (await request.json()) as { identityId?: string }
98+
if ('identityId' in response && response.identityId) return String(response.identityId)
99+
throw new Error(`User registration failed.`)
117100
}
118101

119-
export async function storeUserSession(internalUserId: bigint, session: string) {
120-
if (!session) return
121-
await prisma.cas__user_identity.create({
122-
data: {
123-
user_id: internalUserId,
124-
session: session,
125-
expired: false,
126-
inserted_date: new Date(),
127-
expiration_date: (() => {
128-
const date = new Date()
129-
date.setHours(date.getHours() + 2)
130-
return date
131-
})(),
102+
export async function logoutBrj(session: string) {
103+
const res = await fetch(`https://brj.app/api/v1/customer/logout?apiKey=${process.env['BRJ_API_KEY']}`, {
104+
method: 'POST',
105+
headers: {
106+
'Content-Type': 'application/json',
132107
},
108+
body: JSON.stringify({ apiKey: process.env['BRJ_API_KEY'], identityId: session }),
133109
})
110+
console.log(res)
111+
}
112+
113+
export async function getBrjIdentity(session: string) {
114+
const res = await fetch(
115+
`https://brj.app/api/v1/customer/get-account-info?apiKey=${process.env['BRJ_API_KEY']}&identityId=${session}`,
116+
)
117+
return res.json()
134118
}
135119

136120
export async function invalidateSession(session: string) {

0 commit comments

Comments
 (0)