-
-
Notifications
You must be signed in to change notification settings - Fork 63
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d43d89b
commit 2fb7b72
Showing
6 changed files
with
232 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
/* | ||
* @adonisjs/auth | ||
* | ||
* (c) AdonisJS | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
import debug from './debug.js' | ||
import type { GuardFactory } from './types.js' | ||
import { HttpContextFactory } from '@adonisjs/core/factories/http' | ||
|
||
/** | ||
* Authenticator client is used to create guard instances for | ||
* testing. It passes a fake HTTPContext to the guards, so | ||
* make sure to not call server side APIs that might be | ||
* relying on a real HTTPContext instance | ||
*/ | ||
export class AuthenticatorClient<KnownGuards extends Record<string, GuardFactory>> { | ||
/** | ||
* Registered guards | ||
*/ | ||
#config: { | ||
default: keyof KnownGuards | ||
guards: KnownGuards | ||
} | ||
|
||
/** | ||
* Cache of guards | ||
*/ | ||
#guardsCache: Partial<Record<keyof KnownGuards, unknown>> = {} | ||
|
||
/** | ||
* Name of the default guard | ||
*/ | ||
get defaultGuard(): keyof KnownGuards { | ||
return this.#config.default | ||
} | ||
|
||
constructor(config: { default: keyof KnownGuards; guards: KnownGuards }) { | ||
this.#config = config | ||
debug('creating authenticator client. config %O', this.#config) | ||
} | ||
|
||
/** | ||
* Returns an instance of a known guard. Guards instances are | ||
* cached during the lifecycle of an HTTP request. | ||
*/ | ||
use<Guard extends keyof KnownGuards>(guard?: Guard): ReturnType<KnownGuards[Guard]> { | ||
const guardToUse = guard || this.#config.default | ||
|
||
/** | ||
* Use cached copy if exists | ||
*/ | ||
const cachedGuard = this.#guardsCache[guardToUse] | ||
if (cachedGuard) { | ||
debug('using guard from cache. name: "%s"', guardToUse) | ||
return cachedGuard as ReturnType<KnownGuards[Guard]> | ||
} | ||
|
||
const guardFactory = this.#config.guards[guardToUse] | ||
|
||
/** | ||
* Construct guard and cache it | ||
*/ | ||
debug('creating guard. name: "%s"', guardToUse) | ||
const guardInstance = guardFactory(new HttpContextFactory().create()) | ||
this.#guardsCache[guardToUse] = guardInstance | ||
|
||
return guardInstance as ReturnType<KnownGuards[Guard]> | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
/* | ||
* @adonisjs/auth | ||
* | ||
* (c) AdonisJS | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
/// <reference types="@adonisjs/session/plugins/api_client" /> | ||
|
||
import { ApiClient, ApiRequest } from '@japa/api-client' | ||
import type { ApplicationService } from '@adonisjs/core/types' | ||
import type { Authenticators, GuardContract, GuardFactory } from '../../types.js' | ||
|
||
declare module '@japa/api-client' { | ||
export interface ApiRequest { | ||
authData: { | ||
guard: string | ||
user: unknown | ||
} | ||
|
||
/** | ||
* Login a user using the default authentication | ||
* guard when making an API call | ||
*/ | ||
loginAs(user: { | ||
[K in keyof Authenticators]: Authenticators[K] extends GuardFactory | ||
? ReturnType<Authenticators[K]> extends GuardContract<infer A> | ||
? A | ||
: never | ||
: never | ||
}): this | ||
|
||
/** | ||
* Define the authentication guard for login | ||
*/ | ||
withGuard<K extends keyof Authenticators, Self extends ApiRequest>( | ||
this: Self, | ||
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 | ||
): Self | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Auth API client to authenticate users when making | ||
* 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 | ||
}) | ||
|
||
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) | ||
|
||
if (requestData.headers) { | ||
request.headers(requestData.headers) | ||
} | ||
if (requestData.session) { | ||
request.withSession(requestData.session) | ||
} | ||
if (requestData.cookies) { | ||
request.cookies(requestData.cookies) | ||
} | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters