Skip to content

Commit

Permalink
feat: add browser client plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
thetutlage committed Oct 27, 2023
1 parent 4690505 commit c61447f
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 44 deletions.
14 changes: 13 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"./types": "./build/src/auth/types.js",
"./auth_provider": "./build/providers/auth_provider.js",
"./plugins/api_client": "./build/src/auth/plugins/japa/api_client.js",
"./plugins/browser_client": "./build/src/auth/plugins/japa/browser_client.js",
"./services/main": "./build/services/auth.js",
"./core/token": "./build/src/core/token.js",
"./core/guard_user": "./build/src/core/guard_user.js",
Expand Down Expand Up @@ -78,8 +79,10 @@
"@commitlint/config-conventional": "^18.0.0",
"@japa/api-client": "^2.0.0",
"@japa/assert": "^2.0.0",
"@japa/browser-client": "^2.0.0",
"@japa/expect-type": "^2.0.0",
"@japa/file-system": "^2.0.0",
"@japa/plugin-adonisjs": "^2.0.0",
"@japa/runner": "^3.0.4",
"@japa/snapshot": "^2.0.0",
"@swc/core": "1.3.82",
Expand All @@ -96,6 +99,7 @@
"husky": "^8.0.3",
"luxon": "^3.4.3",
"np": "^8.0.4",
"playwright": "^1.39.0",
"prettier": "^3.0.3",
"set-cookie-parser": "^2.6.0",
"sqlite3": "^5.1.6",
Expand Down Expand Up @@ -140,7 +144,9 @@
"@adonisjs/core": "^6.1.5-31",
"@adonisjs/lucid": "^19.0.0-3",
"@adonisjs/session": "^7.0.0-13",
"@japa/api-client": "^2.0.0"
"@japa/api-client": "^2.0.0",
"@japa/browser-client": "^2.0.0",
"@japa/plugin-adonisjs": "^2.0.0"
},
"peerDependenciesMeta": {
"@adonisjs/lucid": {
Expand All @@ -151,6 +157,12 @@
},
"@japa/api-client": {
"optional": true
},
"@japa/browser-client": {
"optional": true
},
"@japa/plugin-adonisjs": {
"optional": true
}
}
}
100 changes: 57 additions & 43 deletions src/auth/plugins/japa/api_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@

/// <reference types="@adonisjs/session/plugins/api_client" />

import type { PluginFn } from '@japa/runner/types'
import { ApiClient, ApiRequest } from '@japa/api-client'
import type { ApplicationService } from '@adonisjs/core/types'

import debug from '../../debug.js'
import type { Authenticators, GuardContract, GuardFactory } from '../../types.js'

declare module '@japa/api-client' {
Expand Down Expand Up @@ -58,51 +61,62 @@ declare module '@japa/api-client' {
* HTTP requests using the Japa API client
*/
export const authApiClient = (app: ApplicationService) => {
ApiRequest.macro('loginAs', function (this: ApiRequest, user) {
this.authData = {
guard: '__default__',
user: user,
}
return this
})
const pluginFn: PluginFn = function () {
debug('installing auth api client plugin')

ApiRequest.macro('withGuard', function <
K extends keyof Authenticators,
Self extends ApiRequest,
>(this: Self, guard: K) {
return {
loginAs: (user) => {
this.authData = {
guard,
user: user,
}
return this
},
}
})
ApiRequest.macro('loginAs', function (this: ApiRequest, user) {
this.authData = {
guard: '__default__',
user: user,
}
return this
})

/**
* Hook into the request and login the user
*/
ApiClient.setup(async (request) => {
const auth = await app.container.make('auth.manager')
const authData = request['authData']
if (!authData) {
return
}
ApiRequest.macro('withGuard', function <
K extends keyof Authenticators,
Self extends ApiRequest,
>(this: Self, guard: K) {
return {
loginAs: (user) => {
this.authData = {
guard,
user: user,
}
return this
},
}
})

/**
* Hook into the request and login the user
*/
ApiClient.setup(async (request) => {
const auth = await app.container.make('auth.manager')
const authData = request['authData']
if (!authData) {
return
}

const client = auth.createAuthenticatorClient()
const guard = authData.guard === '__default__' ? client.use() : client.use(authData.guard)
const requestData = await (guard as GuardContract<unknown>).authenticateAsClient(authData.user)
const client = auth.createAuthenticatorClient()
const guard = authData.guard === '__default__' ? client.use() : client.use(authData.guard)
const requestData = await (guard as GuardContract<unknown>).authenticateAsClient(
authData.user
)

if (requestData.headers) {
request.headers(requestData.headers)
}
if (requestData.session) {
request.withSession(requestData.session)
}
if (requestData.cookies) {
request.cookies(requestData.cookies)
}
})
if (requestData.headers) {
debug('defining headers with api client request %O', requestData.headers)
request.headers(requestData.headers)
}
if (requestData.session) {
debug('defining session with api client request %O', requestData.session)
request.withSession(requestData.session)
}
if (requestData.cookies) {
debug('defining session with api client request %O', requestData.session)
request.cookies(requestData.cookies)
}
})
}

return pluginFn
}
90 changes: 90 additions & 0 deletions src/auth/plugins/japa/browser_client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* @adoniss/auth
*
* (c) AdonisJS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

/// <reference types="@japa/plugin-adonisjs" />
/// <reference types="@adonisjs/session/plugins/browser_client" />

import { RuntimeException } from '@poppinss/utils'
import type { PluginFn } from '@japa/runner/types'
import { decoratorsCollection } from '@japa/browser-client'
import type { ApplicationService } from '@adonisjs/core/types'

import debug from '../../debug.js'
import type { Authenticators, GuardContract, GuardFactory } from '../../types.js'

declare module 'playwright' {
export interface BrowserContext {
/**
* Login a user using the default authentication
* guard when using the browser context to
* make page visits
*/
loginAs(user: {
[K in keyof Authenticators]: Authenticators[K] extends GuardFactory
? ReturnType<Authenticators[K]> extends GuardContract<infer A>
? A
: never
: never
}): Promise<void>

/**
* Define the authentication guard for login
*/
withGuard<K extends keyof Authenticators>(
guard: K
): {
/**
* Login a user using a specific auth guard
*/
loginAs(
user: Authenticators[K] extends GuardFactory
? ReturnType<Authenticators[K]> extends GuardContract<infer A>
? A
: never
: never
): Promise<void>
}
}
}

export const authBrowserClient = (app: ApplicationService) => {
const pluginFn: PluginFn = async function () {
debug('installing auth browser client plugin')

const auth = await app.container.make('auth.manager')

decoratorsCollection.register({
context(context) {
context.loginAs = async function (user) {
const client = auth.createAuthenticatorClient()
const guard = client.use() as GuardContract<unknown>
const requestData = await guard.authenticateAsClient(user)

if (requestData.headers) {
throw new RuntimeException(`Cannot use "${guard.driverName}" guard with browser client`)
}

if (requestData.cookies) {
debug('defining cookies with browser context %O', requestData.cookies)
Object.keys(requestData.cookies).forEach((cookie) => {
context.setCookie(cookie, requestData.cookies![cookie])
})
}

if (requestData.session) {
debug('defining session with browser context %O', requestData.session)
context.setSession(requestData.session)
}
}
},
})
}

return pluginFn
}

0 comments on commit c61447f

Please sign in to comment.