This commit is contained in:
Tutur33
2023-11-24 22:35:41 +01:00
parent 3c0b507a93
commit 7644b2a0f7
45165 changed files with 4803356 additions and 3 deletions
+9
View File
@@ -0,0 +1,9 @@
# The MIT License
Copyright 2021 Harminder Virk, contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+53
View File
@@ -0,0 +1,53 @@
<div align="center">
<img src="https://res.cloudinary.com/adonisjs/image/upload/q_100/v1558612869/adonis-readme_zscycu.jpg" width="600px">
</div>
<br />
<div align="center">
<h3>Adonis Auth</h3>
<p>The official user authentication package for AdonisJS. Ships with <strong>sessions</strong>, <strong>api tokens</strong> and <strong>basic auth</strong> guards. </p>
</div>
<br />
<div align="center">
[![gh-workflow-image]][gh-workflow-url] [![typescript-image]][typescript-url] [![npm-image]][npm-url] [![license-image]][license-url] [![synk-image]][synk-url]
</div>
<div align="center">
<h3>
<a href="https://preview.adonisjs.com">
Website
</a>
<span> | </span>
<a href="https://preview.adonisjs.com/guides/auth/introduction">
Guides
</a>
<span> | </span>
<a href="CONTRIBUTING.md">
Contributing
</a>
</h3>
</div>
<div align="center">
<sub>Built with ❤︎ by <a href="https://twitter.com/AmanVirk1">Harminder Virk</a>
</div>
[gh-workflow-image]: https://img.shields.io/github/workflow/status/adonisjs/auth/test?style=for-the-badge
[gh-workflow-url]: https://github.com/adonisjs/auth/actions/workflows/test.yml "Github action"
[typescript-image]: https://img.shields.io/badge/Typescript-294E80.svg?style=for-the-badge&logo=typescript
[typescript-url]: "typescript"
[npm-image]: https://img.shields.io/npm/v/@adonisjs/auth/alpha.svg?style=for-the-badge&logo=npm
[npm-url]: https://www.npmjs.com/package/@adonisjs/auth/v/alpha "npm"
[license-image]: https://img.shields.io/npm/l/@adonisjs/auth?color=blueviolet&style=for-the-badge
[license-url]: LICENSE.md "license"
[synk-image]: https://img.shields.io/snyk/vulnerabilities/github/adonisjs/auth?label=Synk%20Vulnerabilities&style=for-the-badge
[synk-url]: https://snyk.io/test/github/adonisjs/auth?targetFile=package.json "synk"
+635
View File
@@ -0,0 +1,635 @@
/// <reference types="@adonisjs/application/build/adonis-typings/application" />
declare module '@ioc:Adonis/Addons/Auth' {
import { DateTime } from 'luxon';
import { HashersList } from '@ioc:Adonis/Core/Hash';
import { QueryClientContract } from '@ioc:Adonis/Lucid/Database';
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
import { ApplicationContract } from '@ioc:Adonis/Core/Application';
import { DatabaseQueryBuilderContract } from '@ioc:Adonis/Lucid/Database';
import { LucidModel, LucidRow, ModelQueryBuilderContract } from '@ioc:Adonis/Lucid/Orm';
/**
* Unwraps user from the provider user
*/
type UnWrapProviderUser<T> = T extends ProviderUserContract<any> ? Exclude<T['user'], null> : T;
/**
* Unwraps awaited type from Promise
*/
type Awaited<T> = T extends PromiseLike<infer U> ? Awaited<U> : T;
/**
* Returns the real user from the provider user
*/
export type GetProviderRealUser<Provider extends keyof ProvidersList> = UnWrapProviderUser<Awaited<ReturnType<ProvidersList[Provider]['implementation']['getUserFor']>>>;
/**
* Provider user works as a bridge between the provider real user
* and the guard. It is never exposed to the end-user.
*/
export interface ProviderUserContract<User extends any> {
user: User | null;
getId(): string | number | null;
verifyPassword: (plainPassword: string) => Promise<boolean>;
getRememberMeToken(): string | null;
setRememberMeToken(token: string): void;
}
/**
* The interface that every provider must implement
*/
export interface UserProviderContract<User extends any> {
/**
* Return an instance of the user wrapped inside the Provider user contract
*/
getUserFor(user: User): Promise<ProviderUserContract<User>>;
/**
* Find a user using the primary key value
*/
findById(id: string | number): Promise<ProviderUserContract<User>>;
/**
* Find a user by searching for their uids
*/
findByUid(uid: string): Promise<ProviderUserContract<User>>;
/**
* Find a user using the remember me token
*/
findByRememberMeToken(userId: string | number, token: string): Promise<ProviderUserContract<User>>;
/**
* Update remember token
*/
updateRememberMeToken(authenticatable: ProviderUserContract<User>): Promise<void>;
}
/**
* Shape of the token sent to/read from the tokens provider
*/
export interface ProviderTokenContract {
/**
* Persisted token value. It is a sha256 hash
*/
tokenHash: string;
/**
* Token name
*/
name: string;
/**
* Token type
*/
type: string;
/**
* UserId for which the token was saved
*/
userId: string | number;
/**
* Expiry date
*/
expiresAt?: DateTime;
/**
* All other token details
*/
meta?: any;
}
/**
* Token providers provides the API to create/fetch and delete tokens
* for a given user. Any token based implementation can use token
* providers, given they only store a single token.
*/
export interface TokenProviderContract {
/**
* Define a custom connection for the driver in use
*/
setConnection(connection: any): this;
/**
* Saves the token to some persistance storage and returns an lookup
* id. We introduced the concept of lookup ids, since lookups by
* cryptographic tokens can have performance impacts on certain
* databases.
*
* Also note that the return lookup id is also prepended to the raw
* token, so that we can later extract the id for reads. The
* real message is to keep the lookup ids small.
*/
write(token: ProviderTokenContract): Promise<string>;
/**
* Find token using the lookup id or the token value
*/
read(lookupId: string, token: string, type: string): Promise<ProviderTokenContract | null>;
/**
* Delete token using the lookup id or the token value
*/
destroy(lookupId: string, type: string): Promise<void>;
}
/**
* Config for the database token provider
*/
export type DatabaseTokenProviderConfig = {
driver: 'database';
table: string;
foreignKey?: string;
connection?: string;
type?: string;
};
/**
* Config for the redis token provider
*/
export type RedisTokenProviderConfig = {
driver: 'redis';
redisConnection: string;
foreignKey?: string;
type?: string;
};
/**
* The shape of the user model accepted by the Lucid provider. The model
* must have `password` and `rememberMeToken` attributes.
*/
export type LucidProviderModel = LucidModel & {
findForAuth?: <T extends LucidModel>(this: T, uids: string[], value: any) => Promise<InstanceType<T>>;
} & {
new (): LucidRow & {
password?: string | null;
rememberMeToken?: string | null;
};
};
/**
* Shape of the lucid provider user builder. It must return [[ProviderUserContract]]
*/
export interface LucidProviderUserBuilder<User extends LucidProviderModel> {
new (user: InstanceType<User> | null, config: LucidProviderConfig<User>, ...args: any[]): ProviderUserContract<InstanceType<User>>;
}
/**
* Lucid provider
*/
export interface LucidProviderContract<User extends LucidProviderModel> extends UserProviderContract<InstanceType<User>> {
/**
* Define a custom connection for all the provider queries
*/
setConnection(connection: string | QueryClientContract): this;
/**
* Before hooks
*/
before(event: 'findUser', callback: (query: ModelQueryBuilderContract<User>) => Promise<void>): this;
/**
* After hooks
*/
after(event: 'findUser', callback: (user: InstanceType<User>) => Promise<void>): this;
}
/**
* The config accepted by the Lucid provider
*/
export type LucidProviderConfig<User extends LucidProviderModel> = {
driver: 'lucid';
model: () => Promise<User> | Promise<{
default: User;
}>;
uids: (keyof InstanceType<User>)[];
identifierKey: string;
connection?: string;
hashDriver?: keyof HashersList;
user?: () => Promise<LucidProviderUserBuilder<User>> | Promise<{
default: LucidProviderUserBuilder<User>;
}>;
};
/**
* Shape of the row returned by the database provider. The table must have `password`
* and `remember_me_token` columns.
*/
export type DatabaseProviderRow = {
password?: string;
remember_me_token?: string;
[key: string]: any;
};
/**
* Shape of database provider user builder. It must always returns [[ProviderUserContract]]
*/
export interface DatabaseProviderUserBuilder {
new (user: DatabaseProviderRow | null, config: DatabaseProviderConfig, ...args: any[]): ProviderUserContract<DatabaseProviderRow>;
}
/**
* Database provider
*/
export interface DatabaseProviderContract<User extends DatabaseProviderRow> extends UserProviderContract<User> {
/**
* Define a custom connection for all the provider queries
*/
setConnection(connection: string | QueryClientContract): this;
/**
* Before hooks
*/
before(event: 'findUser', callback: (query: DatabaseQueryBuilderContract) => Promise<void>): this;
/**
* After hooks
*/
after(event: 'findUser', callback: (user: DatabaseProviderRow) => Promise<void>): this;
}
/**
* The config accepted by the Database provider
*/
export type DatabaseProviderConfig = {
driver: 'database';
uids: string[];
usersTable: string;
identifierKey: string;
connection?: string;
hashDriver?: keyof HashersList;
user?: () => Promise<DatabaseProviderUserBuilder> | Promise<{
default: DatabaseProviderUserBuilder;
}>;
};
/**
* Request data a guard client can set when making the
* testing request
*/
export type ClientRequestData = {
session?: Record<string, any>;
headers?: Record<string, any>;
cookies?: Record<string, any>;
};
/**
* The authentication clients should follow this interface
*/
export interface GuardClientContract<Provider extends keyof ProvidersList> {
/**
* Login a user
*/
login(user: GetProviderRealUser<Provider>, ...args: any[]): Promise<ClientRequestData>;
/**
* Logout user
*/
logout(user: GetProviderRealUser<Provider>): Promise<void>;
}
export interface GuardContract<Provider extends keyof ProvidersList, Guard extends keyof GuardsList> {
name: Guard;
/**
* Reference to the guard config
*/
config: GuardsList[Guard]['config'];
/**
* Reference to the logged in user.
*/
user?: GetProviderRealUser<Provider>;
/**
* Find if the user has been logged out in the current request
*/
isLoggedOut: boolean;
/**
* A boolean to know if user is a guest or not. It is
* always opposite of [[isLoggedIn]]
*/
isGuest: boolean;
/**
* A boolean to know if user is logged in or not
*/
isLoggedIn: boolean;
/**
* A boolean to know if user is retrieved by authenticating
* the current request or not.
*/
isAuthenticated: boolean;
/**
* Whether or not the authentication has been attempted
* for the current request
*/
authenticationAttempted: boolean;
/**
* Reference to the provider for looking up the user
*/
provider: ProvidersList[Provider]['implementation'];
/**
* Verify user credentials.
*/
verifyCredentials(uid: string, password: string): Promise<GetProviderRealUser<Provider>>;
/**
* Attempt to verify user credentials and perform login
*/
attempt(uid: string, password: string, ...args: any[]): Promise<any>;
/**
* Login a user without any verification
*/
login(user: GetProviderRealUser<Provider>, ...args: any[]): Promise<any>;
/**
* Login a user using their id
*/
loginViaId(id: string | number, ...args: any[]): Promise<any>;
/**
* Attempts to authenticate the user for the current HTTP request. An exception
* is raised when unable to do so
*/
authenticate(): Promise<GetProviderRealUser<Provider>>;
/**
* Attempts to authenticate the user for the current HTTP request and supresses
* exceptions raised by the [[authenticate]] method and returns a boolean
*/
check(): Promise<boolean>;
/**
* Logout user
*/
logout(...args: any[]): Promise<void>;
/**
* Serialize guard to JSON
*/
toJSON(): any;
}
/**
* Shape of data emitted by the login event
*/
export type SessionLoginEventData<Provider extends keyof ProvidersList> = {
name: string;
user: GetProviderRealUser<Provider>;
ctx: HttpContextContract;
token: string | null;
};
/**
* Shape of data emitted by the authenticate event
*/
export type SessionAuthenticateEventData<Provider extends keyof ProvidersList> = {
name: string;
user: GetProviderRealUser<Provider>;
ctx: HttpContextContract;
viaRemember: boolean;
};
/**
* Shape of the session guard
*/
export interface SessionGuardContract<Provider extends keyof ProvidersList, Name extends keyof GuardsList> extends GuardContract<Provider, Name> {
/**
* A boolean to know if user is loggedin via remember me token or not.
*/
viaRemember: boolean;
/**
* Attempt to verify user credentials and perform login
*/
attempt(uid: string, password: string, remember?: boolean): Promise<any>;
/**
* Login a user without any verification
*/
login(user: GetProviderRealUser<Provider>, remember?: boolean): Promise<any>;
/**
* Login a user using their id
*/
loginViaId(id: string | number, remember?: boolean): Promise<any>;
/**
* Logout user
*/
logout(renewRememberToken?: boolean): Promise<void>;
}
/**
* Session client to login users during tests
*/
export interface SessionClientContract<Provider extends keyof ProvidersList> extends GuardClientContract<Provider> {
}
/**
* Shape of session driver config.
*/
export type SessionGuardConfig<Provider extends keyof ProvidersList> = {
driver: 'session';
provider: ProvidersList[Provider]['config'];
};
/**
* Shape of data emitted by the authenticate event
*/
export type BasicAuthAuthenticateEventData<Provider extends keyof ProvidersList> = {
name: string;
user: GetProviderRealUser<Provider>;
ctx: HttpContextContract;
};
/**
* Shape of the basic auth guard
*/
export interface BasicAuthGuardContract<Provider extends keyof ProvidersList, Name extends keyof GuardsList> extends Omit<GuardContract<Provider, Name>, 'attempt' | 'login' | 'loginViaId' | 'logout'> {
}
/**
* Basic auth client to login users during tests
*/
export interface BasicAuthClientContract<Provider extends keyof ProvidersList> extends GuardClientContract<Provider> {
}
/**
* Shape of basic auth guard config.
*/
export type BasicAuthGuardConfig<Provider extends keyof ProvidersList> = {
driver: 'basic';
realm?: string;
provider: ProvidersList[Provider]['config'];
};
/**
* Opaque token is generated during the login call by the OpaqueTokensGuard
*/
export interface OpaqueTokenContract<User extends any> {
/**
* Always a bearer token
*/
type: 'bearer';
/**
* The user for which the token was generated
*/
user: User;
/**
* Date/time when the token will be expired
*/
expiresAt?: DateTime;
/**
* Time in seconds until the token is valid
*/
expiresIn?: number;
/**
* Any meta-data attached with the token
*/
meta: any;
/**
* Token name
*/
name: string;
/**
* Token public value
*/
token: string;
/**
* Token hash (persisted to the db as well)
*/
tokenHash: string;
/**
* Serialize token
*/
toJSON(): {
type: 'bearer';
token: string;
expires_at?: string;
expires_in?: number;
};
}
/**
* Login options
*/
export type OATLoginOptions = {
name?: string;
expiresIn?: number | string;
} & {
[key: string]: any;
};
/**
* Shape of data emitted by the login event
*/
export type OATLoginEventData<Provider extends keyof ProvidersList> = {
name: string;
user: GetProviderRealUser<Provider>;
ctx: HttpContextContract;
token: OpaqueTokenContract<GetProviderRealUser<Provider>>;
};
/**
* Shape of the data emitted by the authenticate event
*/
export type OATAuthenticateEventData<Provider extends keyof ProvidersList> = {
name: string;
user: GetProviderRealUser<Provider>;
ctx: HttpContextContract;
token: ProviderTokenContract;
};
/**
* Shape of the OAT guard
*/
export interface OATGuardContract<Provider extends keyof ProvidersList, Name extends keyof GuardsList> extends GuardContract<Provider, Name> {
token?: ProviderTokenContract;
tokenProvider: TokenProviderContract;
/**
* Attempt to verify user credentials and perform login
*/
attempt(uid: string, password: string, options?: OATLoginOptions): Promise<OpaqueTokenContract<GetProviderRealUser<Provider>>>;
/**
* Login a user without any verification
*/
login(user: GetProviderRealUser<Provider>, options?: OATLoginOptions): Promise<OpaqueTokenContract<GetProviderRealUser<Provider>>>;
/**
* Generate token for a user without any verification
*/
generate(user: GetProviderRealUser<Provider>, options?: OATLoginOptions): Promise<OpaqueTokenContract<GetProviderRealUser<Provider>>>;
/**
* Alias for logout
*/
revoke(): Promise<void>;
/**
* Login a user using their id
*/
loginViaId(id: string | number, options?: OATLoginOptions): Promise<OpaqueTokenContract<GetProviderRealUser<Provider>>>;
}
/**
* Oat guard to login users during tests
*/
export interface OATClientContract<Provider extends keyof ProvidersList> extends GuardClientContract<Provider> {
login(user: GetProviderRealUser<Provider>, options?: OATLoginOptions): Promise<ClientRequestData>;
}
/**
* Shape of OAT guard config.
*/
export type OATGuardConfig<Provider extends keyof ProvidersList> = {
/**
* Driver name is always constant
*/
driver: 'oat';
/**
* Provider for managing tokens
*/
tokenProvider: DatabaseTokenProviderConfig | RedisTokenProviderConfig;
/**
* User provider
*/
provider: ProvidersList[Provider]['config'];
};
/**
* List of providers mappings used by the app. Using declaration
* merging, one must extend this interface.
*
* MUST BE SET IN THE USER LAND.
*
* Example:
*
* lucid: {
* config: LucidProviderConfig<any>,
* implementation: LucidProviderContract<any>,
* }
*
*/
export interface ProvidersList {
}
/**
* List of guards mappings used by the app. Using declaration
* merging, one must extend this interface.
*
* MUST BE SET IN THE USER LAND.
*
* Example:
*
* session: {
* config: SessionGuardConfig<'lucid'>,
* implementation: SessionGuardContract<'lucid'>,
* client: SessionClientContract<'lucid'>,
* }
*
*/
export interface GuardsList {
}
/**
* Shape of config accepted by the Auth module. It relies on the
* [[GuardsList]] interface
*/
export type AuthConfig = {
guard: keyof GuardsList;
guards: {
[P in keyof GuardsList]: GuardsList[P]['config'];
};
};
/**
* Instance of the auth contract. The `use` method can be used to obtain
* an instance of a given guard mapping for a single HTTP request
*/
export interface AuthContract extends GuardContract<keyof ProvidersList, keyof GuardsList> {
/**
* The default guard for the current request
*/
defaultGuard: string;
/**
* Use a given guard
*/
use(): GuardContract<keyof ProvidersList, keyof GuardsList>;
use<K extends keyof GuardsList>(guard: K): GuardsList[K]['implementation'];
}
/**
* Shape of the callback accepted to add new user providers
*/
export type ExtendProviderCallback = (auth: AuthManagerContract, mapping: string, config: any) => UserProviderContract<any>;
/**
* Shape of the callback accepted to add new guards
*/
export type ExtendGuardCallback = (auth: AuthManagerContract, mapping: string, config: any, provider: UserProviderContract<any>, ctx: HttpContextContract) => GuardContract<keyof ProvidersList, keyof GuardsList>;
/**
* Shape of the callback accepted to add custom testing
* clients
*/
export type ExtendClientCallback = (auth: AuthManagerContract, mapping: string, config: any, provider: UserProviderContract<any>) => GuardClientContract<keyof ProvidersList>;
/**
* Shape of the auth manager to register custom drivers and providers and
* make instances of them
*/
export interface AuthManagerContract {
application: ApplicationContract;
/**
* The default guard
*/
defaultGuard: string;
/**
* Returns the instance of [[AuthContract]] for a given HTTP request
*/
getAuthForRequest(ctx: HttpContextContract): AuthContract;
/**
* Make instance of a mapping
*/
makeMapping(ctx: HttpContextContract, mapping: string): GuardContract<keyof ProvidersList, keyof GuardsList>;
makeMapping<K extends keyof GuardsList>(ctx: HttpContextContract, mapping: K): GuardsList[K]['implementation'];
/**
* Returns an instance of the auth client for a given
* mapping
*/
client(mapping: string): GuardClientContract<keyof ProvidersList>;
/**
* Extend by adding custom providers, guards and client
*/
extend(type: 'provider', provider: string, callback: ExtendProviderCallback): void;
extend(type: 'guard', guard: string, callback: ExtendGuardCallback): void;
extend(type: 'client', guard: string, callback: ExtendClientCallback): void;
}
const AuthManager: AuthManagerContract;
export default AuthManager;
}
+8
View File
@@ -0,0 +1,8 @@
/*
* @adonisjs/auth
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
+6
View File
@@ -0,0 +1,6 @@
declare module '@ioc:Adonis/Core/Application' {
import { AuthManagerContract } from '@ioc:Adonis/Addons/Auth';
interface ContainerBindings {
'Adonis/Addons/Auth': AuthManagerContract;
}
}
+8
View File
@@ -0,0 +1,8 @@
/*
* @adonisjs/auth
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
+6
View File
@@ -0,0 +1,6 @@
declare module '@ioc:Adonis/Core/HttpContext' {
import { AuthContract } from '@ioc:Adonis/Addons/Auth';
interface HttpContextContract {
auth: AuthContract;
}
}
+8
View File
@@ -0,0 +1,8 @@
/*
* @adonisjs/auth
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
+10
View File
@@ -0,0 +1,10 @@
declare module '@ioc:Adonis/Core/Event' {
import { ProvidersList, OATLoginEventData, SessionLoginEventData, OATAuthenticateEventData, SessionAuthenticateEventData, BasicAuthAuthenticateEventData } from '@ioc:Adonis/Addons/Auth';
interface EventsList {
'adonis:basic:authenticate': BasicAuthAuthenticateEventData<keyof ProvidersList>;
'adonis:session:login': SessionLoginEventData<keyof ProvidersList>;
'adonis:session:authenticate': SessionAuthenticateEventData<keyof ProvidersList>;
'adonis:api:authenticate': OATAuthenticateEventData<keyof ProvidersList>;
'adonis:api:login': OATLoginEventData<keyof ProvidersList>;
}
}
+8
View File
@@ -0,0 +1,8 @@
/*
* @adonisjs/auth
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
+5
View File
@@ -0,0 +1,5 @@
/// <reference path="auth.d.ts" />
/// <reference path="context.d.ts" />
/// <reference path="events.d.ts" />
/// <reference path="container.d.ts" />
/// <reference path="tests.d.ts" />
+13
View File
@@ -0,0 +1,13 @@
/*
* @adonisjs/auth
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/// <reference path="./auth.ts" />
/// <reference path="./context.ts" />
/// <reference path="./events.ts" />
/// <reference path="./container.ts" />
/// <reference path="./tests.ts" />
+23
View File
@@ -0,0 +1,23 @@
import '@japa/api-client';
import { GuardsList, ProvidersList, AuthManagerContract, GetProviderRealUser } from '@ioc:Adonis/Addons/Auth';
declare module '@japa/api-client' {
interface ApiRequest {
/**
* Auth manager reference
*/
authManager: AuthManagerContract;
/**
* Switch guard to login during the request
*/
guard<K extends keyof GuardsList, Self>(this: Self, guard: K): {
/**
* Login as a user
*/
loginAs(...args: Parameters<GuardsList[K]['client']['login']>): Self;
};
/**
* Login as a user
*/
loginAs(user: GetProviderRealUser<keyof ProvidersList>): this;
}
}
+11
View File
@@ -0,0 +1,11 @@
"use strict";
/*
* @adonisjs/auth
*
* (c) AdonisJS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
require("@japa/api-client");
+338
View File
@@ -0,0 +1,338 @@
"use strict";
/*
* @adonisjs/auth
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
const path_1 = require("path");
const helpers_1 = require("@poppinss/utils/build/helpers");
// const USER_MIGRATION_TIME_PREFIX = '1587988332388'
// const TOKENS_MIGRATION_TIME_PREFIX = '1592489784670'
/**
* Base path to contract stub partials
*/
const CONTRACTS_PARTIALS_BASE = './contract/partials';
/**
* Base path to config stub partials
*/
const CONFIG_PARTIALS_BASE = './config/partials';
/**
* Prompt choices for the provider selection
*/
const PROVIDER_PROMPT_CHOICES = [
{
name: 'lucid',
message: 'Lucid',
hint: ' (Uses Data Models)',
},
{
name: 'database',
message: 'Database',
hint: ' (Uses Database QueryBuilder)',
},
];
/**
* Prompt choices for the guard selection
*/
const GUARD_PROMPT_CHOICES = [
{
name: 'web',
message: 'Web',
hint: ' (Uses sessions for managing auth state)',
},
{
name: 'api',
message: 'API tokens',
hint: ' (Uses database backed opaque tokens)',
},
{
name: 'basic',
message: 'Basic Auth',
hint: ' (Uses HTTP Basic auth for authenticating requests)',
},
];
/**
* Prompt choices for the tokens provider selection
*/
const TOKENS_PROVIDER_PROMPT_CHOICES = [
{
name: 'database',
message: 'Database',
hint: ' (Uses SQL table for storing API tokens)',
},
{
name: 'redis',
message: 'Redis',
hint: ' (Uses Redis for storing API tokens)',
},
];
/**
* Returns absolute path to the stub relative from the templates
* directory
*/
function getStub(...relativePaths) {
return (0, path_1.join)(__dirname, 'templates', ...relativePaths);
}
/**
* Creates the model file
*/
function makeModel(projectRoot, app, sink, state) {
const modelsDirectory = app.resolveNamespaceDirectory('models') || 'app/Models';
const modelPath = (0, path_1.join)(modelsDirectory, `${state.modelName}.ts`);
const template = new sink.files.MustacheFile(projectRoot, modelPath, getStub('model.txt'));
if (template.exists()) {
sink.logger.action('create').skipped(`${modelPath} file already exists`);
return;
}
template.apply(state).commit();
sink.logger.action('create').succeeded(modelPath);
}
/**
* Create the migration file
*/
function makeUsersMigration(projectRoot, app, sink, state) {
const migrationsDirectory = app.directoriesMap.get('migrations') || 'database';
const migrationPath = (0, path_1.join)(migrationsDirectory, `${Date.now()}_${state.usersTableName}.ts`);
const template = new sink.files.MustacheFile(projectRoot, migrationPath, getStub('migrations/auth.txt'));
if (template.exists()) {
sink.logger.action('create').skipped(`${migrationPath} file already exists`);
return;
}
template.apply(state).commit();
sink.logger.action('create').succeeded(migrationPath);
}
/**
* Create the migration file
*/
function makeTokensMigration(projectRoot, app, sink, state) {
const migrationsDirectory = app.directoriesMap.get('migrations') || 'database';
const migrationPath = (0, path_1.join)(migrationsDirectory, `${Date.now()}_${state.tokensTableName}.ts`);
const template = new sink.files.MustacheFile(projectRoot, migrationPath, getStub('migrations/api_tokens.txt'));
if (template.exists()) {
sink.logger.action('create').skipped(`${migrationPath} file already exists`);
return;
}
template.apply(state).commit();
sink.logger.action('create').succeeded(migrationPath);
}
/**
* Create the middleware(s)
*/
function makeMiddleware(projectRoot, app, sink, state) {
const middlewareDirectory = app.resolveNamespaceDirectory('middleware') || 'app/Middleware';
/**
* Auth middleware
*/
const authPath = (0, path_1.join)(middlewareDirectory, 'Auth.ts');
const authTemplate = new sink.files.MustacheFile(projectRoot, authPath, getStub('middleware/Auth.txt'));
if (authTemplate.exists()) {
sink.logger.action('create').skipped(`${authPath} file already exists`);
}
else {
authTemplate.apply(state).commit();
sink.logger.action('create').succeeded(authPath);
}
/**
* Silent auth middleware
*/
const silentAuthPath = (0, path_1.join)(middlewareDirectory, 'SilentAuth.ts');
const silentAuthTemplate = new sink.files.MustacheFile(projectRoot, silentAuthPath, getStub('middleware/SilentAuth.txt'));
if (silentAuthTemplate.exists()) {
sink.logger.action('create').skipped(`${silentAuthPath} file already exists`);
}
else {
silentAuthTemplate.apply(state).commit();
sink.logger.action('create').succeeded(silentAuthPath);
}
}
/**
* Creates the contract file
*/
function makeContract(projectRoot, app, sink, state) {
const contractsDirectory = app.directoriesMap.get('contracts') || 'contracts';
const contractPath = (0, path_1.join)(contractsDirectory, 'auth.ts');
const template = new sink.files.MustacheFile(projectRoot, contractPath, getStub('contract/auth.txt'));
template.overwrite = true;
const partials = {
provider: getStub(CONTRACTS_PARTIALS_BASE, `user-provider-${state.provider}.txt`),
};
state.guards.forEach((guard) => {
partials[`${guard}_guard`] = getStub(CONTRACTS_PARTIALS_BASE, `${guard}-guard.txt`);
});
template.apply(state).partials(partials).commit();
sink.logger.action('create').succeeded(contractPath);
}
/**
* Makes the auth config file
*/
function makeConfig(projectRoot, app, sink, state) {
const configDirectory = app.directoriesMap.get('config') || 'config';
const configPath = (0, path_1.join)(configDirectory, 'auth.ts');
const template = new sink.files.MustacheFile(projectRoot, configPath, getStub('config/auth.txt'));
template.overwrite = true;
const partials = {
provider: getStub(CONFIG_PARTIALS_BASE, `user-provider-${state.provider}.txt`),
token_provider: getStub(CONFIG_PARTIALS_BASE, `tokens-provider-${state.tokensProvider}.txt`),
};
state.guards.forEach((guard) => {
partials[`${guard}_guard`] = getStub(CONFIG_PARTIALS_BASE, `${guard}-guard.txt`);
});
template.apply(state).partials(partials).commit();
sink.logger.action('create').succeeded(configPath);
}
/**
* Prompts user to select the provider
*/
async function getProvider(sink) {
return sink.getPrompt().choice('Select provider for finding users', PROVIDER_PROMPT_CHOICES, {
validate(choice) {
return choice && choice.length ? true : 'Select the provider for finding users';
},
});
}
/**
* Prompts user to select the tokens provider
*/
async function getTokensProvider(sink) {
return sink
.getPrompt()
.choice('Select the provider for storing API tokens', TOKENS_PROVIDER_PROMPT_CHOICES, {
validate(choice) {
return choice && choice.length ? true : 'Select the provider for storing API tokens';
},
});
}
/**
* Prompts user to select one or more guards
*/
async function getGuard(sink) {
return sink
.getPrompt()
.multiple('Select which guard you need for authentication (select using space)', GUARD_PROMPT_CHOICES, {
validate(choices) {
return choices && choices.length
? true
: 'Select one or more guards for authenticating users';
},
});
}
/**
* Prompts user for the model name
*/
async function getModelName(sink) {
return sink.getPrompt().ask('Enter model name to be used for authentication', {
validate(value) {
return !!value.trim().length;
},
});
}
/**
* Prompts user for the table name
*/
async function getTableName(sink) {
return sink.getPrompt().ask('Enter the database table name to look up users', {
validate(value) {
return !!value.trim().length;
},
});
}
/**
* Prompts user for the table name
*/
async function getMigrationConsent(sink, tableName) {
return sink
.getPrompt()
.confirm(`Create migration for the ${sink.logger.colors.underline(tableName)} table?`);
}
/**
* Instructions to be executed when setting up the package.
*/
async function instructions(projectRoot, app, sink) {
const state = {
usersTableName: '',
tokensTableName: 'api_tokens',
tokensSchemaName: 'ApiTokens',
usersSchemaName: '',
provider: 'lucid',
tokensProvider: 'database',
guards: [],
hasGuard: {
web: false,
api: false,
basic: false,
},
};
state.provider = await getProvider(sink);
state.guards = await getGuard(sink);
/**
* Need booleans for mustache templates
*/
state.guards.forEach((guard) => (state.hasGuard[guard] = true));
/**
* Make model when provider is lucid otherwise prompt for the database
* table name
*/
if (state.provider === 'lucid') {
const modelName = await getModelName(sink);
state.modelName = helpers_1.string.pascalCase(helpers_1.string.singularize(modelName.replace(/(\.ts|\.js)$/, '')));
state.usersTableName = helpers_1.string.pluralize(helpers_1.string.snakeCase(state.modelName));
state.modelReference = helpers_1.string.camelCase(helpers_1.string.singularize(state.modelName));
state.modelNamespace = `${app.namespacesMap.get('models') || 'App/Models'}/${state.modelName}`;
}
else {
state.usersTableName = await getTableName(sink);
}
const usersMigrationConsent = await getMigrationConsent(sink, state.usersTableName);
let tokensMigrationConsent = false;
/**
* Only ask for the consent when using the api guard
*/
if (state.hasGuard.api) {
state.tokensProvider = await getTokensProvider(sink);
if (state.tokensProvider === 'database') {
tokensMigrationConsent = await getMigrationConsent(sink, state.tokensTableName);
}
}
/**
* Pascal case
*/
const camelCaseSchemaName = helpers_1.string.camelCase(`${state.usersTableName}_schema`);
state.usersSchemaName = `${camelCaseSchemaName
.charAt(0)
.toUpperCase()}${camelCaseSchemaName.slice(1)}`;
/**
* Make model when prompted for it
*/
if (state.modelName) {
makeModel(projectRoot, app, sink, state);
}
/**
* Make users migration file
*/
if (usersMigrationConsent) {
makeUsersMigration(projectRoot, app, sink, state);
}
/**
* Make tokens migration file
*/
if (tokensMigrationConsent) {
makeTokensMigration(projectRoot, app, sink, state);
}
/**
* Make contract file
*/
makeContract(projectRoot, app, sink, state);
/**
* Make config file
*/
makeConfig(projectRoot, app, sink, state);
/**
* Make middleware
*/
makeMiddleware(projectRoot, app, sink, state);
}
exports.default = instructions;
+30
View File
@@ -0,0 +1,30 @@
/// <reference types="@adonisjs/application/build/adonis-typings/application" />
import { ApplicationContract } from '@ioc:Adonis/Core/Application';
/**
* Auth provider to register the auth binding
*/
export default class AuthProvider {
protected application: ApplicationContract;
constructor(application: ApplicationContract);
static needsApplication: boolean;
/**
* Register auth binding
*/
register(): void;
/**
* Sharing the auth object with HTTP context
*/
protected registerAuthWithHttpContext(): void;
/**
* Sharing auth with all the templates
*/
protected shareAuthWithViews(): void;
/**
* Register test bindings
*/
protected registerTestBindings(): void;
/**
* Hook into boot to register auth macro
*/
boot(): Promise<void>;
}
+69
View File
@@ -0,0 +1,69 @@
"use strict";
/*
* @adonisjs/auth
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
/**
* Auth provider to register the auth binding
*/
class AuthProvider {
constructor(application) {
this.application = application;
}
/**
* Register auth binding
*/
register() {
this.application.container.singleton('Adonis/Addons/Auth', () => {
const authConfig = this.application.container
.resolveBinding('Adonis/Core/Config')
.get('auth', {});
const { AuthManager } = require('../src/AuthManager');
return new AuthManager(this.application, authConfig);
});
}
/**
* Sharing the auth object with HTTP context
*/
registerAuthWithHttpContext() {
this.application.container.withBindings(['Adonis/Core/HttpContext', 'Adonis/Addons/Auth'], (HttpContext, Auth) => {
HttpContext.getter('auth', function auth() {
return Auth.getAuthForRequest(this);
}, true);
});
}
/**
* Sharing auth with all the templates
*/
shareAuthWithViews() {
this.application.container.withBindings(['Adonis/Core/Server', 'Adonis/Core/View'], (Server) => {
Server.hooks.before(async (ctx) => {
ctx['view'].share({ auth: ctx.auth });
});
});
}
/**
* Register test bindings
*/
registerTestBindings() {
this.application.container.withBindings(['Japa/Preset/ApiRequest', 'Japa/Preset/ApiClient', 'Adonis/Addons/Auth'], (ApiRequest, ApiClient, Auth) => {
const { defineTestsBindings } = require('../src/Bindings/Tests');
return defineTestsBindings(ApiRequest, ApiClient, Auth);
});
}
/**
* Hook into boot to register auth macro
*/
async boot() {
this.registerAuthWithHttpContext();
this.shareAuthWithViews();
this.registerTestBindings();
}
}
exports.default = AuthProvider;
AuthProvider.needsApplication = true;
+97
View File
@@ -0,0 +1,97 @@
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
import { AuthContract, AuthManagerContract } from '@ioc:Adonis/Addons/Auth';
/**
* Auth class exposes the API to obtain guard instances for a given
* HTTP request.
*/
export declare class Auth implements AuthContract {
private manager;
private ctx;
/**
* We keep a per request singleton instances for each instantiated mapping
*/
private mappingsCache;
/**
* The default guard is always the one defined inside the config, until
* manually overwritten by the user
*/
defaultGuard: string;
constructor(manager: AuthManagerContract, ctx: HttpContextContract);
/**
* Returns an instance of a named or the default mapping
*/
use(mapping?: string): any;
/**
* Guard name for the default mapping
*/
get name(): any;
/**
* Reference to the logged in user
*/
get user(): any;
/**
* Reference to the default guard config
*/
get config(): any;
/**
* Find if the user has been logged out in the current request
*/
get isLoggedOut(): any;
/**
* A boolean to know if user is a guest or not. It is
* always opposite of [[isLoggedIn]]
*/
get isGuest(): any;
/**
* A boolean to know if user is logged in or not
*/
get isLoggedIn(): any;
/**
* A boolean to know if user is retrieved by authenticating
* the current request or not.
*/
get isAuthenticated(): any;
/**
* Whether or not the authentication has been attempted
* for the current request
*/
get authenticationAttempted(): any;
/**
* Reference to the provider for looking up the user
*/
get provider(): any;
/**
* Verify user credentials.
*/
verifyCredentials(uid: string, password: string): Promise<any>;
/**
* Attempt to verify user credentials and perform login
*/
attempt(uid: string, password: string, ...args: any[]): Promise<any>;
/**
* Login a user without any verification
*/
login(user: any, ...args: any[]): Promise<any>;
/**
* Login a user using their id
*/
loginViaId(id: string | number, ...args: any[]): Promise<any>;
/**
* Attempts to authenticate the user for the current HTTP request. An exception
* is raised when unable to do so
*/
authenticate(): Promise<any>;
/**
* Attempts to authenticate the user for the current HTTP request and supresses
* exceptions raised by the [[authenticate]] method and returns a boolean
*/
check(): Promise<any>;
/**
* Logout user
*/
logout(...args: any[]): Promise<any>;
/**
* Serialize toJSON
*/
toJSON(): any;
}
+155
View File
@@ -0,0 +1,155 @@
"use strict";
/*
* @adonisjs/auth
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Auth = void 0;
/**
* Auth class exposes the API to obtain guard instances for a given
* HTTP request.
*/
class Auth {
constructor(manager, ctx) {
this.manager = manager;
this.ctx = ctx;
/**
* We keep a per request singleton instances for each instantiated mapping
*/
this.mappingsCache = new Map();
/**
* The default guard is always the one defined inside the config, until
* manually overwritten by the user
*/
this.defaultGuard = this.manager.defaultGuard;
}
/**
* Returns an instance of a named or the default mapping
*/
use(mapping) {
mapping = mapping || this.defaultGuard;
if (!this.mappingsCache.has(mapping)) {
this.ctx.logger.trace('instantiating auth mapping', { name: mapping });
this.mappingsCache.set(mapping, this.manager.makeMapping(this.ctx, mapping));
}
return this.mappingsCache.get(mapping);
}
/**
* Guard name for the default mapping
*/
get name() {
return this.use().name;
}
/**
* Reference to the logged in user
*/
get user() {
return this.use().user;
}
/**
* Reference to the default guard config
*/
get config() {
return this.use().config;
}
/**
* Find if the user has been logged out in the current request
*/
get isLoggedOut() {
return this.use().isLoggedOut;
}
/**
* A boolean to know if user is a guest or not. It is
* always opposite of [[isLoggedIn]]
*/
get isGuest() {
return this.use().isGuest;
}
/**
* A boolean to know if user is logged in or not
*/
get isLoggedIn() {
return this.use().isLoggedIn;
}
/**
* A boolean to know if user is retrieved by authenticating
* the current request or not.
*/
get isAuthenticated() {
return this.use().isAuthenticated;
}
/**
* Whether or not the authentication has been attempted
* for the current request
*/
get authenticationAttempted() {
return this.use().authenticationAttempted;
}
/**
* Reference to the provider for looking up the user
*/
get provider() {
return this.use().provider;
}
/**
* Verify user credentials.
*/
async verifyCredentials(uid, password) {
return this.use().verifyCredentials(uid, password);
}
/**
* Attempt to verify user credentials and perform login
*/
async attempt(uid, password, ...args) {
return this.use().attempt(uid, password, ...args);
}
/**
* Login a user without any verification
*/
async login(user, ...args) {
return this.use().login(user, ...args);
}
/**
* Login a user using their id
*/
async loginViaId(id, ...args) {
return this.use().loginViaId(id, ...args);
}
/**
* Attempts to authenticate the user for the current HTTP request. An exception
* is raised when unable to do so
*/
async authenticate() {
return this.use().authenticate();
}
/**
* Attempts to authenticate the user for the current HTTP request and supresses
* exceptions raised by the [[authenticate]] method and returns a boolean
*/
async check() {
return this.use().check();
}
/**
* Logout user
*/
async logout(...args) {
return this.use().logout(...args);
}
/**
* Serialize toJSON
*/
toJSON() {
return {
defaultGuard: this.defaultGuard,
guards: [...this.mappingsCache.keys()].reduce((result, key) => {
result[key] = this.mappingsCache.get(key).toJSON();
return result;
}, {}),
};
}
}
exports.Auth = Auth;
+117
View File
@@ -0,0 +1,117 @@
/// <reference types="@adonisjs/application/build/adonis-typings/application" />
import { AuthConfig, GuardsList, AuthManagerContract, ExtendGuardCallback, ExtendProviderCallback, ExtendClientCallback } from '@ioc:Adonis/Addons/Auth';
import { ApplicationContract } from '@ioc:Adonis/Core/Application';
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
/**
* Auth manager to manage guards and providers object. The extend API can
* be used to add custom guards and providers
*/
export declare class AuthManager implements AuthManagerContract {
application: ApplicationContract;
private config;
/**
* Extended set of testing clients
*/
private extendedClients;
/**
* Extended set of providers
*/
private extendedProviders;
/**
* Extend set of guards
*/
private extendedGuards;
/**
* Reference to the default guard
*/
defaultGuard: keyof GuardsList;
constructor(application: ApplicationContract, config: AuthConfig);
/**
* Verifies and returns an instance of the event emitter
*/
private getEmitter;
/**
* Lazily makes an instance of the lucid provider
*/
private makeLucidProvider;
/**
* Lazily makes an instance of the database provider
*/
private makeDatabaseProvider;
/**
* Returns an instance of the extended provider
*/
private makeExtendedProvider;
/**
* Lazily makes an instance of the token database provider
*/
private makeTokenDatabaseProvider;
/**
* Lazily makes an instance of the token redis provider
*/
private makeTokenRedisProvider;
/**
* Returns an instance of the session guard
*/
private makeSessionGuard;
/**
* Returns an instance of the session guard
*/
private makeOatGuard;
/**
* Returns an instance of the basic auth guard
*/
private makeBasicAuthGuard;
/**
* Returns an instance of the extended guard
*/
private makeExtendedGuard;
/**
* Returns an instance of the session client
*/
private makeSessionClient;
/**
* Returns an instance of the session client
*/
private makeOatClient;
/**
* Returns an instance of the extended client
*/
private makeExtendedClient;
/**
* Makes client instance for the defined driver inside the
* mapping config.
*/
private makeClientInstance;
/**
* Makes instance of a provider based upon the driver value
*/
private makeUserProviderInstance;
/**
* Makes instance of a provider based upon the driver value
*/
private makeTokenProviderInstance;
/**
* Makes guard instance for the defined driver inside the
* mapping config.
*/
private makeGuardInstance;
/**
* Make an instance of a given mapping for the current HTTP request.
*/
makeMapping(ctx: HttpContextContract, mapping: keyof GuardsList): any;
/**
* Returns an instance of the testing
*/
client(mapping: keyof GuardsList): any;
/**
* Returns an instance of the auth class for the current request
*/
getAuthForRequest(ctx: HttpContextContract): any;
/**
* Extend auth by adding custom providers and guards
*/
extend(type: 'provider', name: string, callback: ExtendProviderCallback): void;
extend(type: 'guard', name: string, callback: ExtendGuardCallback): void;
extend(type: 'client', name: string, callback: ExtendClientCallback): void;
}
+262
View File
@@ -0,0 +1,262 @@
"use strict";
/*
* @adonisjs/auth
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.AuthManager = void 0;
const utils_1 = require("@poppinss/utils");
const Auth_1 = require("../Auth");
/**
* Auth manager to manage guards and providers object. The extend API can
* be used to add custom guards and providers
*/
class AuthManager {
constructor(application, config) {
this.application = application;
this.config = config;
/**
* Extended set of testing clients
*/
this.extendedClients = new Map();
/**
* Extended set of providers
*/
this.extendedProviders = new Map();
/**
* Extend set of guards
*/
this.extendedGuards = new Map();
/**
* Reference to the default guard
*/
this.defaultGuard = this.config.guard;
const validator = new utils_1.ManagerConfigValidator(config, 'auth', 'config/auth');
validator.validateDefault('guard');
validator.validateList('guards', 'guard');
}
/**
* Verifies and returns an instance of the event emitter
*/
getEmitter() {
const hasEmitter = this.application.container.hasBinding('Adonis/Core/Event');
if (!hasEmitter) {
throw new utils_1.Exception('"Adonis/Core/Event" is required by the auth provider');
}
return this.application.container.use('Adonis/Core/Event');
}
/**
* Lazily makes an instance of the lucid provider
*/
makeLucidProvider(config) {
return new (require('../UserProviders/Lucid').LucidProvider)(this.application, config);
}
/**
* Lazily makes an instance of the database provider
*/
makeDatabaseProvider(config) {
const Database = this.application.container.use('Adonis/Lucid/Database');
return new (require('../UserProviders/Database').DatabaseProvider)(this.application, config, Database);
}
/**
* Returns an instance of the extended provider
*/
makeExtendedProvider(mapping, config) {
const providerCallback = this.extendedProviders.get(config.driver);
if (!providerCallback) {
throw new utils_1.Exception(`Invalid provider "${config.driver}"`);
}
return providerCallback(this, mapping, config);
}
/**
* Lazily makes an instance of the token database provider
*/
makeTokenDatabaseProvider(config) {
const Database = this.application.container.use('Adonis/Lucid/Database');
return new (require('../TokenProviders/Database').TokenDatabaseProvider)(config, Database);
}
/**
* Lazily makes an instance of the token redis provider
*/
makeTokenRedisProvider(config) {
if (!this.application.container.hasBinding('Adonis/Addons/Redis')) {
throw new utils_1.Exception('"@adonisjs/redis" is required to use the "redis" token provider');
}
const Redis = this.application.container.use('Adonis/Addons/Redis');
return new (require('../TokenProviders/Redis').TokenRedisProvider)(config, Redis);
}
/**
* Returns an instance of the session guard
*/
makeSessionGuard(mapping, config, provider, ctx) {
const { SessionGuard } = require('../Guards/Session');
return new SessionGuard(mapping, config, this.getEmitter(), provider, ctx);
}
/**
* Returns an instance of the session guard
*/
makeOatGuard(mapping, config, provider, ctx) {
const { OATGuard } = require('../Guards/Oat');
const tokenProvider = this.makeTokenProviderInstance(config.tokenProvider);
return new OATGuard(mapping, config, this.getEmitter(), provider, ctx, tokenProvider);
}
/**
* Returns an instance of the basic auth guard
*/
makeBasicAuthGuard(mapping, config, provider, ctx) {
const { BasicAuthGuard } = require('../Guards/BasicAuth');
return new BasicAuthGuard(mapping, config, this.getEmitter(), provider, ctx);
}
/**
* Returns an instance of the extended guard
*/
makeExtendedGuard(mapping, config, provider, ctx) {
const guardCallback = this.extendedGuards.get(config.driver);
if (!guardCallback) {
throw new utils_1.Exception(`Invalid guard driver "${config.driver}" property`);
}
return guardCallback(this, mapping, config, provider, ctx);
}
/**
* Returns an instance of the session client
*/
makeSessionClient(mapping, config, provider) {
const { SessionClient } = require('../Clients/Session');
return new SessionClient(mapping, config, provider);
}
/**
* Returns an instance of the session client
*/
makeOatClient(mapping, config, provider) {
const { OATClient } = require('../Clients/Oat');
const tokenProvider = this.makeTokenProviderInstance(config.tokenProvider);
return new OATClient(mapping, config, provider, tokenProvider);
}
/**
* Returns an instance of the extended client
*/
makeExtendedClient(mapping, config, provider) {
const clientCallback = this.extendedClients.get(config.driver);
if (!clientCallback) {
throw new utils_1.Exception(`Invalid guard driver "${config.driver}" property`);
}
return clientCallback(this, mapping, config, provider);
}
/**
* Makes client instance for the defined driver inside the
* mapping config.
*/
makeClientInstance(mapping, mappingConfig, provider) {
if (!mappingConfig || !mappingConfig.driver) {
throw new utils_1.Exception('Invalid auth config, missing "driver" property');
}
switch (mappingConfig.driver) {
case 'session':
return this.makeSessionClient(mapping, mappingConfig, provider);
case 'oat':
return this.makeOatClient(mapping, mappingConfig, provider);
case 'basic':
throw new utils_1.Exception('There is no testing client for basic auth. Use "request.basicAuth" method instead');
default:
return this.makeExtendedClient(mapping, mappingConfig, provider);
}
}
/**
* Makes instance of a provider based upon the driver value
*/
makeUserProviderInstance(mapping, providerConfig) {
if (!providerConfig || !providerConfig.driver) {
throw new utils_1.Exception('Invalid auth config, missing "provider" or "provider.driver" property');
}
switch (providerConfig.driver) {
case 'lucid':
return this.makeLucidProvider(providerConfig);
case 'database':
return this.makeDatabaseProvider(providerConfig);
default:
return this.makeExtendedProvider(mapping, providerConfig);
}
}
/**
* Makes instance of a provider based upon the driver value
*/
makeTokenProviderInstance(providerConfig) {
if (!providerConfig || !providerConfig.driver) {
throw new utils_1.Exception('Invalid auth config, missing "tokenProvider" or "tokenProvider.driver" property');
}
switch (providerConfig.driver) {
case 'database':
return this.makeTokenDatabaseProvider(providerConfig);
case 'redis':
return this.makeTokenRedisProvider(providerConfig);
default:
throw new utils_1.Exception(`Invalid token provider "${providerConfig.driver}"`);
}
}
/**
* Makes guard instance for the defined driver inside the
* mapping config.
*/
makeGuardInstance(mapping, mappingConfig, provider, ctx) {
if (!mappingConfig || !mappingConfig.driver) {
throw new utils_1.Exception('Invalid auth config, missing "driver" property');
}
switch (mappingConfig.driver) {
case 'session':
return this.makeSessionGuard(mapping, mappingConfig, provider, ctx);
case 'oat':
return this.makeOatGuard(mapping, mappingConfig, provider, ctx);
case 'basic':
return this.makeBasicAuthGuard(mapping, mappingConfig, provider, ctx);
default:
return this.makeExtendedGuard(mapping, mappingConfig, provider, ctx);
}
}
/**
* Make an instance of a given mapping for the current HTTP request.
*/
makeMapping(ctx, mapping) {
const mappingConfig = this.config.guards[mapping];
if (mappingConfig === undefined) {
throw new utils_1.Exception(`Invalid guard "${mapping}". Make sure the guard is defined inside the config/auth file`);
}
const provider = this.makeUserProviderInstance(mapping, mappingConfig.provider);
return this.makeGuardInstance(mapping, mappingConfig, provider, ctx);
}
/**
* Returns an instance of the testing
*/
client(mapping) {
const mappingConfig = this.config.guards[mapping];
if (mappingConfig === undefined) {
throw new utils_1.Exception(`Invalid guard "${mapping}". Make sure the guard is defined inside the config/auth file`);
}
const provider = this.makeUserProviderInstance(mapping, mappingConfig.provider);
return this.makeClientInstance(mapping, mappingConfig, provider);
}
/**
* Returns an instance of the auth class for the current request
*/
getAuthForRequest(ctx) {
return new Auth_1.Auth(this, ctx);
}
extend(type, name, callback) {
if (type === 'provider') {
this.extendedProviders.set(name, callback);
return;
}
if (type === 'client') {
this.extendedClients.set(name, callback);
return;
}
if (type === 'guard') {
this.extendedGuards.set(name, callback);
return;
}
}
}
exports.AuthManager = AuthManager;
+6
View File
@@ -0,0 +1,6 @@
import { AuthManagerContract } from '@ioc:Adonis/Addons/Auth';
import { ContainerBindings } from '@ioc:Adonis/Core/Application';
/**
* Define test bindings
*/
export declare function defineTestsBindings(ApiRequest: ContainerBindings['Japa/Preset/ApiRequest'], ApiClient: ContainerBindings['Japa/Preset/ApiClient'], AuthManager: AuthManagerContract): void;
+69
View File
@@ -0,0 +1,69 @@
"use strict";
/*
* @adonisjs/auth
*
* (c) AdonisJS Auth
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.defineTestsBindings = void 0;
/**
* Define test bindings
*/
function defineTestsBindings(ApiRequest, ApiClient, AuthManager) {
/**
* Set "sessionClient" on the api request
*/
ApiRequest.getter('authManager', function () {
return AuthManager;
}, true);
/**
* Login user using the default guard
*/
ApiRequest.macro('loginAs', function (user) {
this['authData'] = {
client: this.authManager.client(this.authManager.defaultGuard),
args: [user],
};
return this;
});
/**
* Login user using a custom guard
*/
ApiRequest.macro('guard', function (mapping) {
return {
loginAs: (...args) => {
this['authData'] = {
client: this.authManager.client(mapping),
args,
};
return this;
},
};
});
/**
* Hook into the request and login the user
*/
ApiClient.setup(async (request) => {
const authData = request['authData'];
if (!authData) {
return;
}
const requestData = await authData.client.login(...authData.args);
if (requestData.headers) {
request.headers(requestData.headers);
}
if (requestData.session) {
request.session(requestData.session);
}
if (requestData.cookies) {
request.cookies(requestData.cookies);
}
return async () => {
await authData.client.logout(...authData.args);
};
});
}
exports.defineTestsBindings = defineTestsBindings;
+50
View File
@@ -0,0 +1,50 @@
import { OATGuardConfig, OATLoginOptions, OATClientContract, UserProviderContract, ClientRequestData } from '@ioc:Adonis/Addons/Auth';
import { TokenProviderContract } from '@ioc:Adonis/Addons/Auth';
/**
* OAT client to login a user during tests using the
* opaque tokens guard
*/
export declare class OATClient implements OATClientContract<any> {
name: string;
config: OATGuardConfig<any>;
private provider;
tokenProvider: TokenProviderContract;
constructor(name: string, config: OATGuardConfig<any>, provider: UserProviderContract<any>, tokenProvider: TokenProviderContract);
/**
* Token generated during the login call
*/
private tokenId?;
/**
* Length of the raw token. The hash length will vary
*/
private tokenLength;
/**
* Token type for the persistance store
*/
private tokenType;
/**
* Returns the provider user instance from the regular user details. Raises
* exception when id is missing
*/
private getUserForLogin;
/**
* Converts value to a sha256 hash
*/
private generateHash;
/**
* Converts expiry duration to an absolute date/time value
*/
private getExpiresAtDate;
/**
* Generates a new token + hash for the persistance
*/
private generateTokenForPersistance;
/**
* Returns the request data to mark user as logged in
*/
login(user: any, options?: OATLoginOptions): Promise<ClientRequestData>;
/**
* Logout user
*/
logout(): Promise<void>;
}
+123
View File
@@ -0,0 +1,123 @@
"use strict";
/*
* @adonisjs/auth
*
* (c) AdonisJS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.OATClient = void 0;
const luxon_1 = require("luxon");
const crypto_1 = require("crypto");
const utils_1 = require("@poppinss/utils");
const helpers_1 = require("@poppinss/utils/build/helpers");
const ProviderToken_1 = require("../../Tokens/ProviderToken");
/**
* OAT client to login a user during tests using the
* opaque tokens guard
*/
class OATClient {
constructor(name, config, provider, tokenProvider) {
this.name = name;
this.config = config;
this.provider = provider;
this.tokenProvider = tokenProvider;
/**
* Length of the raw token. The hash length will vary
*/
this.tokenLength = 60;
/**
* Token type for the persistance store
*/
this.tokenType = this.config.tokenProvider.type || 'opaque_token';
}
/**
* Returns the provider user instance from the regular user details. Raises
* exception when id is missing
*/
async getUserForLogin(user, identifierKey) {
const providerUser = await this.provider.getUserFor(user);
/**
* Ensure id exists on the user
*/
const id = providerUser.getId();
if (!id) {
throw new utils_1.Exception(`Cannot login user. Value of "${identifierKey}" is not defined`);
}
return providerUser;
}
/**
* Converts value to a sha256 hash
*/
generateHash(token) {
return (0, crypto_1.createHash)('sha256').update(token).digest('hex');
}
/**
* Converts expiry duration to an absolute date/time value
*/
getExpiresAtDate(expiresIn) {
if (!expiresIn) {
return;
}
const milliseconds = typeof expiresIn === 'string' ? helpers_1.string.toMs(expiresIn) : expiresIn;
return luxon_1.DateTime.local().plus({ milliseconds });
}
/**
* Generates a new token + hash for the persistance
*/
generateTokenForPersistance(expiresIn) {
const token = helpers_1.string.generateRandom(this.tokenLength);
return {
token,
hash: this.generateHash(token),
expiresAt: this.getExpiresAtDate(expiresIn),
};
}
/**
* Returns the request data to mark user as logged in
*/
async login(user, options) {
/**
* Normalize options with defaults
*/
const { expiresIn, name, ...meta } = Object.assign({
name: 'Opaque Access Token',
}, options);
/**
* Since the login method is not exposed to the end user, we cannot expect
* them to instantiate and pass an instance of provider user, so we
* create one manually.
*/
const providerUser = await this.getUserForLogin(user, this.config.provider.identifierKey);
/**
* "getUserForLogin" raises exception when id is missing, so we can
* safely assume it is defined
*/
const id = providerUser.getId();
const token = this.generateTokenForPersistance(expiresIn);
/**
* Persist token to the database. Make sure that we are always
* passing the hash to the storage driver
*/
const providerToken = new ProviderToken_1.ProviderToken(name, token.hash, id, this.tokenType);
providerToken.expiresAt = token.expiresAt;
providerToken.meta = meta;
this.tokenId = await this.tokenProvider.write(providerToken);
return {
headers: {
Authorization: `Bearer ${helpers_1.base64.urlEncode(this.tokenId)}.${token.token}`,
},
};
}
/**
* Logout user
*/
async logout() {
if (this.tokenId) {
await this.tokenProvider.destroy(this.tokenId, this.tokenType);
}
}
}
exports.OATClient = OATClient;
+34
View File
@@ -0,0 +1,34 @@
import { SessionGuardConfig, GuardClientContract, UserProviderContract, ProviderUserContract } from '@ioc:Adonis/Addons/Auth';
/**
* Session client to login a user during tests using the
* sessions guard
*/
export declare class SessionClient implements GuardClientContract<any> {
name: string;
private config;
private provider;
constructor(name: string, config: SessionGuardConfig<any>, provider: UserProviderContract<any>);
/**
* The name of the session key name
*/
get sessionKeyName(): string;
/**
* Returns the provider user instance from the regular user details. Raises
* exception when id is missing
*/
protected getUserForLogin(user: any, identifierKey: string): Promise<ProviderUserContract<any>>;
/**
* Returns the request data to mark user as logged in
*/
login(user: any): Promise<{
session: {
[x: string]: string | number;
};
}>;
/**
* No need to logout when using session client.
* Session data is persisted within memory and will
* be cleared after each test
*/
logout(): Promise<void>;
}
+72
View File
@@ -0,0 +1,72 @@
"use strict";
/*
* @adonisjs/auth
*
* (c) AdonisJS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.SessionClient = void 0;
const utils_1 = require("@poppinss/utils");
/**
* Session client to login a user during tests using the
* sessions guard
*/
class SessionClient {
constructor(name, config, provider) {
this.name = name;
this.config = config;
this.provider = provider;
}
/**
* The name of the session key name
*/
get sessionKeyName() {
return `auth_${this.name}`;
}
/**
* Returns the provider user instance from the regular user details. Raises
* exception when id is missing
*/
async getUserForLogin(user, identifierKey) {
const providerUser = await this.provider.getUserFor(user);
/**
* Ensure id exists on the user
*/
const id = providerUser.getId();
if (!id) {
throw new utils_1.Exception(`Cannot login user. Value of "${identifierKey}" is not defined`);
}
return providerUser;
}
/**
* Returns the request data to mark user as logged in
*/
async login(user) {
/**
* Since the login method is exposed to the end user, we cannot expect
* them to instantiate and return an instance of authenticatable, so
* we create one manually.
*/
const providerUser = await this.getUserForLogin(user, this.config.provider.identifierKey);
/**
* getUserForLogin raises exception when id is missing, so we can
* safely assume it is defined
*/
const id = providerUser.getId();
return {
session: {
[this.sessionKeyName]: id,
},
};
}
/**
* No need to logout when using session client.
* Session data is persisted within memory and will
* be cleared after each test
*/
async logout() { }
}
exports.SessionClient = SessionClient;
@@ -0,0 +1,47 @@
import { Exception } from '@poppinss/utils';
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
/**
* Exception raised when unable to authenticate user session
*/
export declare class AuthenticationException extends Exception {
guard: string;
redirectTo: string;
responseText: string;
/**
* Raise exception with message and redirect url
*/
constructor(message: string, code: string, guard?: string, redirectTo?: string);
/**
* Prompts user to enter credentials
*/
protected respondWithBasicAuthPrompt(ctx: HttpContextContract, realm?: string): void;
/**
* Send response as an array of errors
*/
protected respondWithJson(ctx: HttpContextContract): void;
/**
* Flash error message and redirect the user back
*/
protected respondWithRedirect(ctx: HttpContextContract): void;
/**
* Send response as an array of errors formatted as per JSONAPI spec
*/
protected respondWithJsonAPI(ctx: HttpContextContract): void;
/**
* Missing session or unable to lookup user from session
*/
static invalidSession(guard: string): AuthenticationException;
/**
* Missing/Invalid token or unable to lookup user from the token
*/
static invalidToken(guard: string): AuthenticationException;
/**
* Missing or invalid basic auth credentials
*/
static invalidBasicCredentials(guard: string): AuthenticationException;
/**
* Self handle exception and attempt to make the best response based
* upon the type of request
*/
handle(_: AuthenticationException, ctx: HttpContextContract): Promise<void>;
}
@@ -0,0 +1,142 @@
"use strict";
/*
* @adonisjs/auth
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.AuthenticationException = void 0;
const utils_1 = require("@poppinss/utils");
/**
* Exception raised when unable to authenticate user session
*/
class AuthenticationException extends utils_1.Exception {
/**
* Raise exception with message and redirect url
*/
constructor(message, code, guard, redirectTo) {
super(message, 401, code);
this.redirectTo = '/login';
this.responseText = this.message;
if (redirectTo) {
this.redirectTo = redirectTo;
}
if (guard) {
this.guard = guard;
}
}
/**
* Prompts user to enter credentials
*/
respondWithBasicAuthPrompt(ctx, realm) {
realm = realm || 'Authenticate';
ctx.response
.status(this.status)
.header('WWW-Authenticate', `Basic realm="${realm}", charset="UTF-8"`)
.send(this.responseText);
}
/**
* Send response as an array of errors
*/
respondWithJson(ctx) {
ctx.response.status(this.status).send({
errors: [
{
message: this.responseText,
},
],
});
}
/**
* Flash error message and redirect the user back
*/
respondWithRedirect(ctx) {
if (!ctx.session) {
return ctx.response.status(this.status).send(this.responseText);
}
ctx.session.flashExcept(['_csrf']);
ctx.session.flash('auth', { error: this.responseText });
ctx.response.redirect(this.redirectTo, true);
}
/**
* Send response as an array of errors formatted as per JSONAPI spec
*/
respondWithJsonAPI(ctx) {
ctx.response.status(this.status).send({
errors: [
{
code: this.code,
title: this.responseText,
source: null,
},
],
});
}
/**
* Missing session or unable to lookup user from session
*/
static invalidSession(guard) {
return new this('Invalid session', 'E_INVALID_AUTH_SESSION', guard);
}
/**
* Missing/Invalid token or unable to lookup user from the token
*/
static invalidToken(guard) {
return new this('Invalid API token', 'E_INVALID_API_TOKEN', guard);
}
/**
* Missing or invalid basic auth credentials
*/
static invalidBasicCredentials(guard) {
return new this('Invalid basic auth credentials', 'E_INVALID_BASIC_CREDENTIALS', guard);
}
/**
* Self handle exception and attempt to make the best response based
* upon the type of request
*/
async handle(_, ctx) {
/**
* We need access to the guard config and driver to make appropriate response
*/
const config = this.guard ? ctx.auth.use(this.guard).config : null;
/**
* Use translation when using i18n
*/
if ('i18n' in ctx) {
this.responseText = ctx.i18n.formatMessage(`auth.${this.code}`, {}, this.message);
}
/**
* Show username, password prompt when using basic auth driver
*/
if (config && config.driver === 'basic') {
this.respondWithBasicAuthPrompt(ctx, config.realm);
return;
}
/**
* Respond with json for ajax requests
*/
if (ctx.request.ajax()) {
this.respondWithJson(ctx);
return;
}
/**
* Uses content negotiation to make the response
*/
switch (ctx.request.accepts(['html', 'application/vnd.api+json', 'json'])) {
case 'html':
case null:
this.respondWithRedirect(ctx);
break;
case 'json':
this.respondWithJson(ctx);
break;
case 'application/vnd.api+json':
this.respondWithJsonAPI(ctx);
break;
}
}
}
exports.AuthenticationException = AuthenticationException;
@@ -0,0 +1,34 @@
import { Exception } from '@poppinss/utils';
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
/**
* Exception raised when unable to verify user credentials
*/
export declare class InvalidCredentialsException extends Exception {
guard: string;
responseText: string;
/**
* Unable to find user
*/
static invalidUid(guard: string): InvalidCredentialsException;
/**
* Invalid user password
*/
static invalidPassword(guard: string): InvalidCredentialsException;
/**
* Send response as an array of errors
*/
protected respondWithJson(ctx: HttpContextContract): void;
/**
* Flash error message and redirect the user back
*/
protected respondWithRedirect(ctx: HttpContextContract): void;
/**
* Send response as an array of errors formatted as per JSONAPI spec
*/
protected respondWithJsonAPI(ctx: HttpContextContract): void;
/**
* Self handle exception and attempt to make the best response based
* upon the type of request
*/
handle(_: InvalidCredentialsException, ctx: HttpContextContract): Promise<void>;
}
@@ -0,0 +1,112 @@
"use strict";
/*
* @adonisjs/auth
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.InvalidCredentialsException = void 0;
const utils_1 = require("@poppinss/utils");
/**
* Exception raised when unable to verify user credentials
*/
class InvalidCredentialsException extends utils_1.Exception {
constructor() {
super(...arguments);
this.responseText = this.message;
}
/**
* Unable to find user
*/
static invalidUid(guard) {
const error = new this('User not found', 400, 'E_INVALID_AUTH_UID');
error.guard = guard;
return error;
}
/**
* Invalid user password
*/
static invalidPassword(guard) {
const error = new this('Password mis-match', 400, 'E_INVALID_AUTH_PASSWORD');
error.guard = guard;
return error;
}
/**
* Send response as an array of errors
*/
respondWithJson(ctx) {
ctx.response.status(this.status).send({
errors: [
{
message: this.responseText,
},
],
});
}
/**
* Flash error message and redirect the user back
*/
respondWithRedirect(ctx) {
if (!ctx.session) {
return ctx.response.status(this.status).send(this.responseText);
}
ctx.session.flashExcept(['_csrf']);
ctx.session.flash('auth', {
error: this.responseText,
/**
* Will be removed in the future
*/
errors: {
uid: this.code === 'E_INVALID_AUTH_UID' ? ['Invalid login id'] : null,
password: this.code === 'E_INVALID_AUTH_PASSWORD' ? ['Invalid password'] : null,
},
});
ctx.response.redirect('back', true);
}
/**
* Send response as an array of errors formatted as per JSONAPI spec
*/
respondWithJsonAPI(ctx) {
ctx.response.status(this.status).send({
errors: [
{
code: this.code,
title: this.responseText,
source: null,
},
],
});
}
/**
* Self handle exception and attempt to make the best response based
* upon the type of request
*/
async handle(_, ctx) {
/**
* Use translation when using i18n
*/
if ('i18n' in ctx) {
this.responseText = ctx.i18n.formatMessage(`auth.${this.code}`, {}, this.message);
}
if (ctx.request.ajax()) {
this.respondWithJson(ctx);
return;
}
switch (ctx.request.accepts(['html', 'application/vnd.api+json', 'json'])) {
case 'html':
case null:
this.respondWithRedirect(ctx);
break;
case 'json':
this.respondWithJson(ctx);
break;
case 'application/vnd.api+json':
this.respondWithJsonAPI(ctx);
break;
}
}
}
exports.InvalidCredentialsException = InvalidCredentialsException;
+75
View File
@@ -0,0 +1,75 @@
import { UserProviderContract, ProviderUserContract, GuardsList } from '@ioc:Adonis/Addons/Auth';
/**
* Base guard with shared abilities
*/
export declare abstract class BaseGuard<Guard extends keyof GuardsList> {
name: Guard;
config: GuardsList[Guard]['config'];
provider: UserProviderContract<any>;
constructor(name: Guard, config: GuardsList[Guard]['config'], provider: UserProviderContract<any>);
/**
* Reference to the name of the guard driver
*/
get driver(): "basic" | "session" | "oat";
/**
* Whether or not the authentication has been attempted
* for the current request
*/
authenticationAttempted: boolean;
/**
* Find if the user has been logged out in the current request
*/
isLoggedOut: boolean;
/**
* A boolean to know if user is retrieved by authenticating
* the current request or not
*/
isAuthenticated: boolean;
/**
* A boolean to know if user is loggedin via remember me token
* or not.
*/
viaRemember: boolean;
/**
* Logged in or authenticated user
*/
user?: any;
/**
* Accessor to know if user is logged in
*/
get isLoggedIn(): boolean;
/**
* Accessor to know if user is a guest. It is always opposite
* of [[isLoggedIn]]
*/
get isGuest(): boolean;
/**
* Lookup user using UID
*/
private lookupUsingUid;
/**
* Verify user password
*/
private verifyPassword;
/**
* Finds user by their id and returns the provider user instance
*/
protected findById(id: string | number): Promise<ProviderUserContract<any>>;
/**
* Returns the provider user instance from the regular user details. Raises
* exception when id is missing
*/
protected getUserForLogin(user: any, identifierKey: string): Promise<ProviderUserContract<any>>;
/**
* Marks user as logged-in
*/
protected markUserAsLoggedIn(user: any, authenticated?: boolean, viaRemember?: boolean): void;
/**
* Marks the user as logged out
*/
protected markUserAsLoggedOut(): void;
/**
* Verifies user credentials
*/
verifyCredentials(uid: string, password: string): Promise<any>;
}
+138
View File
@@ -0,0 +1,138 @@
"use strict";
/*
* @adonisjs/auth
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.BaseGuard = void 0;
const utils_1 = require("@poppinss/utils");
const InvalidCredentialsException_1 = require("../../Exceptions/InvalidCredentialsException");
/**
* Base guard with shared abilities
*/
class BaseGuard {
constructor(name, config, provider) {
this.name = name;
this.config = config;
this.provider = provider;
/**
* Whether or not the authentication has been attempted
* for the current request
*/
this.authenticationAttempted = false;
/**
* Find if the user has been logged out in the current request
*/
this.isLoggedOut = false;
/**
* A boolean to know if user is retrieved by authenticating
* the current request or not
*/
this.isAuthenticated = false;
/**
* A boolean to know if user is loggedin via remember me token
* or not.
*/
this.viaRemember = false;
}
/**
* Reference to the name of the guard driver
*/
get driver() {
return this.config.driver;
}
/**
* Accessor to know if user is logged in
*/
get isLoggedIn() {
return !!this.user;
}
/**
* Accessor to know if user is a guest. It is always opposite
* of [[isLoggedIn]]
*/
get isGuest() {
return !this.isLoggedIn;
}
/**
* Lookup user using UID
*/
async lookupUsingUid(uid) {
const providerUser = await this.provider.findByUid(uid);
if (!providerUser.user) {
throw InvalidCredentialsException_1.InvalidCredentialsException.invalidUid(this.name);
}
return providerUser;
}
/**
* Verify user password
*/
async verifyPassword(providerUser, password) {
/**
* Verify password or raise exception
*/
const verified = await providerUser.verifyPassword(password);
if (!verified) {
throw InvalidCredentialsException_1.InvalidCredentialsException.invalidPassword(this.name);
}
}
/**
* Finds user by their id and returns the provider user instance
*/
async findById(id) {
const providerUser = await this.provider.findById(id);
if (!providerUser.user) {
throw InvalidCredentialsException_1.InvalidCredentialsException.invalidUid(this.name);
}
return providerUser;
}
/**
* Returns the provider user instance from the regular user details. Raises
* exception when id is missing
*/
async getUserForLogin(user, identifierKey) {
const providerUser = await this.provider.getUserFor(user);
/**
* Ensure id exists on the user
*/
const id = providerUser.getId();
if (!id) {
throw new utils_1.Exception(`Cannot login user. Value of "${identifierKey}" is not defined`);
}
return providerUser;
}
/**
* Marks user as logged-in
*/
markUserAsLoggedIn(user, authenticated, viaRemember) {
this.user = user;
this.isLoggedOut = false;
authenticated && (this.isAuthenticated = true);
viaRemember && (this.viaRemember = true);
}
/**
* Marks the user as logged out
*/
markUserAsLoggedOut() {
this.isLoggedOut = true;
this.isAuthenticated = false;
this.viaRemember = false;
this.user = null;
}
/**
* Verifies user credentials
*/
async verifyCredentials(uid, password) {
if (!uid || !password) {
throw InvalidCredentialsException_1.InvalidCredentialsException.invalidUid(this.name);
}
const providerUser = await this.lookupUsingUid(uid);
await this.verifyPassword(providerUser, password);
return providerUser.user;
}
}
exports.BaseGuard = BaseGuard;
+67
View File
@@ -0,0 +1,67 @@
/// <reference types="@adonisjs/events/build/adonis-typings" />
import { EmitterContract } from '@ioc:Adonis/Core/Event';
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
import { UserProviderContract, BasicAuthGuardConfig, BasicAuthGuardContract } from '@ioc:Adonis/Addons/Auth';
import { BaseGuard } from '../Base';
/**
* Basic auth guard enables user login using basic auth headers.
*/
export declare class BasicAuthGuard extends BaseGuard<any> implements BasicAuthGuardContract<any, any> {
private emitter;
private ctx;
constructor(name: string, config: BasicAuthGuardConfig<any>, emitter: EmitterContract, provider: UserProviderContract<any>, ctx: HttpContextContract);
/**
* Returns data packet for the authenticate event. Arguments are
*
* - The mapping identifier
* - Logged in user
* - HTTP context
*/
private getAuthenticateEventData;
/**
* Returns user credentials by parsing the HTTP "Authorization" header
*/
private getCredentials;
/**
* Returns user for the uid and password.
*/
private getUser;
/**
* Implemented method to raise exception when someone calls this method
* without selecting the guard explicitly
*/
attempt(): Promise<any>;
/**
* Implemented method to raise exception when someone calls this method
* without selecting the guard explicitly
*/
loginViaId(): Promise<void>;
/**
* Implemented method to raise exception when someone calls this method
* without selecting the guard explicitly
*/
login(): Promise<void>;
/**
* Authenticates the current HTTP request by checking for the HTTP
* "Authorization" header
*/
authenticate(): Promise<any>;
/**
* Same as [[authenticate]] but returns a boolean over raising exceptions
*/
check(): Promise<boolean>;
/**
* Logout by clearing session and cookies
*/
logout(): Promise<void>;
/**
* Serialize toJSON for JSON.stringify
*/
toJSON(): {
isLoggedIn: boolean;
isGuest: boolean;
authenticationAttempted: boolean;
isAuthenticated: boolean;
user: any;
};
}
+181
View File
@@ -0,0 +1,181 @@
"use strict";
/*
* @adonisjs/auth
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.BasicAuthGuard = void 0;
const utils_1 = require("@poppinss/utils");
const helpers_1 = require("@poppinss/utils/build/helpers");
const Base_1 = require("../Base");
const AuthenticationException_1 = require("../../Exceptions/AuthenticationException");
/**
* RegExp for basic auth credentials.
* Copy/pasted from https://github.com/jshttp/basic-auth/blob/master/index.js
*
* credentials = auth-scheme 1*SP token68
* auth-scheme = "Basic" ; case insensitive
* token68 = 1*( ALPHA / DIGIT / "-" / "." / "_" / "~" / "+" / "/" ) *"="
*/
const CREDENTIALS_REGEXP = /^ *(?:[Bb][Aa][Ss][Ii][Cc]) +([A-Za-z0-9._~+/-]+=*) *$/;
/**
* RegExp for basic auth user/pass
* Copy/pasted from https://github.com/jshttp/basic-auth/blob/master/index.js
*
* user-pass = userid ":" password
* userid = *<TEXT excluding ":">
* password = *TEXT
*/
const USER_PASS_REGEXP = /^([^:]*):(.*)$/;
/**
* Basic auth guard enables user login using basic auth headers.
*/
class BasicAuthGuard extends Base_1.BaseGuard {
constructor(name, config, emitter, provider, ctx) {
super(name, config, provider);
this.emitter = emitter;
this.ctx = ctx;
}
/**
* Returns data packet for the authenticate event. Arguments are
*
* - The mapping identifier
* - Logged in user
* - HTTP context
*/
getAuthenticateEventData(user) {
return {
name: this.name,
ctx: this.ctx,
user,
};
}
/**
* Returns user credentials by parsing the HTTP "Authorization" header
*/
getCredentials() {
/**
* Ensure the "Authorization" header value exists
*/
const credentials = this.ctx.request.header('Authorization');
if (!credentials) {
throw AuthenticationException_1.AuthenticationException.invalidBasicCredentials(this.name);
}
/**
* Ensure credentials are in correct format
*/
const match = CREDENTIALS_REGEXP.exec(credentials);
if (!match) {
throw AuthenticationException_1.AuthenticationException.invalidBasicCredentials(this.name);
}
/**
* Ensure credentials are base64 encoded
*/
const decoded = helpers_1.base64.decode(match[1], 'utf-8', true);
if (!decoded) {
throw AuthenticationException_1.AuthenticationException.invalidBasicCredentials(this.name);
}
/**
* Ensure decoded credentials are in correct format
*/
const user = USER_PASS_REGEXP.exec(decoded);
if (!user) {
throw AuthenticationException_1.AuthenticationException.invalidBasicCredentials(this.name);
}
return { uid: user[1], password: user[2] };
}
/**
* Returns user for the uid and password.
*/
async getUser(uid, password) {
try {
return await this.verifyCredentials(uid, password);
}
catch {
throw AuthenticationException_1.AuthenticationException.invalidBasicCredentials(this.name);
}
}
/**
* Implemented method to raise exception when someone calls this method
* without selecting the guard explicitly
*/
async attempt() {
return this.login();
}
/**
* Implemented method to raise exception when someone calls this method
* without selecting the guard explicitly
*/
async loginViaId() {
return this.login();
}
/**
* Implemented method to raise exception when someone calls this method
* without selecting the guard explicitly
*/
async login() {
throw new utils_1.Exception('There is no concept of login in basic auth', 500);
}
/**
* Authenticates the current HTTP request by checking for the HTTP
* "Authorization" header
*/
async authenticate() {
if (this.authenticationAttempted) {
return this.user;
}
this.authenticationAttempted = true;
/**
* Parse HTTP "Authorization" header to get credentials
*/
const credentials = this.getCredentials();
/**
* Pull user from credentials
*/
const user = await this.getUser(credentials.uid, credentials.password);
/**
* Mark user a logged in
*/
this.markUserAsLoggedIn(user, true);
/**
* Emit event
*/
this.emitter.emit('adonis:basic:authenticate', this.getAuthenticateEventData(user));
return this.user;
}
/**
* Same as [[authenticate]] but returns a boolean over raising exceptions
*/
async check() {
try {
await this.authenticate();
}
catch (error) {
this.ctx.logger.trace(error, 'Authentication failure');
}
return this.isAuthenticated;
}
/**
* Logout by clearing session and cookies
*/
async logout() {
throw new utils_1.Exception('There is no concept of logout in basic auth', 500);
}
/**
* Serialize toJSON for JSON.stringify
*/
toJSON() {
return {
isLoggedIn: this.isLoggedIn,
isGuest: this.isGuest,
authenticationAttempted: this.authenticationAttempted,
isAuthenticated: this.isAuthenticated,
user: this.user,
};
}
}
exports.BasicAuthGuard = BasicAuthGuard;
+149
View File
@@ -0,0 +1,149 @@
/// <reference types="@adonisjs/events/build/adonis-typings" />
import { EmitterContract } from '@ioc:Adonis/Core/Event';
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
import { OATGuardConfig, OATLoginOptions, OATGuardContract, UserProviderContract, ProviderTokenContract, TokenProviderContract } from '@ioc:Adonis/Addons/Auth';
import { BaseGuard } from '../Base';
/**
* Exposes the API to generate and authenticate HTTP request using
* opaque tokens
*/
export declare class OATGuard extends BaseGuard<any> implements OATGuardContract<any, any> {
config: OATGuardConfig<any>;
private emitter;
private ctx;
tokenProvider: TokenProviderContract;
constructor(name: string, config: OATGuardConfig<any>, emitter: EmitterContract, provider: UserProviderContract<any>, ctx: HttpContextContract, tokenProvider: TokenProviderContract);
/**
* Reference to the parsed token
*/
private parsedToken?;
/**
* Length of the raw token. The hash length will vary
*/
private tokenLength;
/**
* Token type for the persistance store
*/
private tokenType;
/**
* Whether or not the authentication has been attempted
* for the current request
*/
authenticationAttempted: boolean;
/**
* Find if the user has been logged out in the current request
*/
isLoggedOut: boolean;
/**
* A boolean to know if user is retrieved by authenticating
* the current request or not
*/
isAuthenticated: boolean;
/**
* Logged in or authenticated user
*/
user?: any;
/**
* Token fetched as part of the authenticate or the login
* call
*/
token?: ProviderTokenContract;
/**
* Accessor to know if user is logged in
*/
get isLoggedIn(): boolean;
/**
* Accessor to know if user is a guest. It is always opposite
* of [[isLoggedIn]]
*/
get isGuest(): boolean;
/**
* Converts value to a sha256 hash
*/
private generateHash;
/**
* Converts expiry duration to an absolute date/time value
*/
private getExpiresAtDate;
/**
* Generates a new token + hash for the persistance
*/
private generateTokenForPersistance;
/**
* Returns data packet for the login event. Arguments are
*
* - The mapping identifier
* - Logged in user
* - HTTP context
* - API token
*/
private getLoginEventData;
/**
* Returns data packet for the authenticate event. Arguments are
*
* - The mapping identifier
* - Logged in user
* - HTTP context
* - A boolean to tell if logged in viaRemember or not
*/
private getAuthenticateEventData;
/**
* Parses the token received in the request. The method also performs
* some initial level of sanity checks.
*/
private parsePublicToken;
/**
* Returns the bearer token
*/
private getBearerToken;
/**
* Returns the token by reading it from the token provider
*/
private getProviderToken;
/**
* Returns user from the user session id
*/
private getUserById;
/**
* Verify user credentials and perform login
*/
attempt(uid: string, password: string, options?: OATLoginOptions): Promise<any>;
/**
* Login user using their id
*/
loginViaId(id: string | number, options?: OATLoginOptions): Promise<any>;
/**
* Generate token for a user. It is merely an alias for `login`
*/
generate(user: any, options?: OATLoginOptions): Promise<any>;
/**
* Login a user
*/
login(user: any, options?: OATLoginOptions): Promise<any>;
/**
* Authenticates the current HTTP request by checking for the bearer token
*/
authenticate(): Promise<any>;
/**
* Same as [[authenticate]] but returns a boolean over raising exceptions
*/
check(): Promise<boolean>;
/**
* Alias for the logout method
*/
revoke(): Promise<void>;
/**
* Logout by removing the token from the storage
*/
logout(): Promise<void>;
/**
* Serialize toJSON for JSON.stringify
*/
toJSON(): {
isLoggedIn: boolean;
isGuest: boolean;
authenticationAttempted: boolean;
isAuthenticated: boolean;
user: any;
};
}
+347
View File
@@ -0,0 +1,347 @@
"use strict";
/*
* @adonisjs/auth
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.OATGuard = void 0;
const luxon_1 = require("luxon");
const crypto_1 = require("crypto");
const helpers_1 = require("@poppinss/utils/build/helpers");
const Base_1 = require("../Base");
const OpaqueToken_1 = require("../../Tokens/OpaqueToken");
const ProviderToken_1 = require("../../Tokens/ProviderToken");
const AuthenticationException_1 = require("../../Exceptions/AuthenticationException");
/**
* Exposes the API to generate and authenticate HTTP request using
* opaque tokens
*/
class OATGuard extends Base_1.BaseGuard {
constructor(name, config, emitter, provider, ctx, tokenProvider) {
super(name, config, provider);
this.config = config;
this.emitter = emitter;
this.ctx = ctx;
this.tokenProvider = tokenProvider;
/**
* Length of the raw token. The hash length will vary
*/
this.tokenLength = 60;
/**
* Token type for the persistance store
*/
this.tokenType = this.config.tokenProvider.type || 'opaque_token';
/**
* Whether or not the authentication has been attempted
* for the current request
*/
this.authenticationAttempted = false;
/**
* Find if the user has been logged out in the current request
*/
this.isLoggedOut = false;
/**
* A boolean to know if user is retrieved by authenticating
* the current request or not
*/
this.isAuthenticated = false;
}
/**
* Accessor to know if user is logged in
*/
get isLoggedIn() {
return !!this.user;
}
/**
* Accessor to know if user is a guest. It is always opposite
* of [[isLoggedIn]]
*/
get isGuest() {
return !this.isLoggedIn;
}
/**
* Converts value to a sha256 hash
*/
generateHash(token) {
return (0, crypto_1.createHash)('sha256').update(token).digest('hex');
}
/**
* Converts expiry duration to an absolute date/time value
*/
getExpiresAtDate(expiresIn) {
if (!expiresIn) {
return;
}
const milliseconds = typeof expiresIn === 'string' ? helpers_1.string.toMs(expiresIn) : expiresIn;
return luxon_1.DateTime.local().plus({ milliseconds });
}
/**
* Generates a new token + hash for the persistance
*/
generateTokenForPersistance(expiresIn) {
const token = helpers_1.string.generateRandom(this.tokenLength);
return {
token,
hash: this.generateHash(token),
expiresAt: this.getExpiresAtDate(expiresIn),
};
}
/**
* Returns data packet for the login event. Arguments are
*
* - The mapping identifier
* - Logged in user
* - HTTP context
* - API token
*/
getLoginEventData(user, token) {
return {
name: this.name,
ctx: this.ctx,
user,
token,
};
}
/**
* Returns data packet for the authenticate event. Arguments are
*
* - The mapping identifier
* - Logged in user
* - HTTP context
* - A boolean to tell if logged in viaRemember or not
*/
getAuthenticateEventData(user, token) {
return {
name: this.name,
ctx: this.ctx,
user,
token,
};
}
/**
* Parses the token received in the request. The method also performs
* some initial level of sanity checks.
*/
parsePublicToken(token) {
const parts = token.split('.');
/**
* Ensure the token has two parts
*/
if (parts.length !== 2) {
throw AuthenticationException_1.AuthenticationException.invalidToken(this.name);
}
/**
* Ensure the first part is a base64 encode id
*/
const tokenId = helpers_1.base64.urlDecode(parts[0], undefined, true);
if (!tokenId) {
throw AuthenticationException_1.AuthenticationException.invalidToken(this.name);
}
/**
* Ensure 2nd part of the token has the expected length
*/
if (parts[1].length !== this.tokenLength) {
throw AuthenticationException_1.AuthenticationException.invalidToken(this.name);
}
/**
* Set parsed token
*/
this.parsedToken = {
tokenId,
value: parts[1],
};
return this.parsedToken;
}
/**
* Returns the bearer token
*/
getBearerToken() {
/**
* Ensure the "Authorization" header value exists
*/
const token = this.ctx.request.header('Authorization');
if (!token) {
throw AuthenticationException_1.AuthenticationException.invalidToken(this.name);
}
/**
* Ensure that token has minimum of two parts and the first
* part is a constant string named `bearer`
*/
const [type, value] = token.split(' ');
if (!type || type.toLowerCase() !== 'bearer' || !value) {
throw AuthenticationException_1.AuthenticationException.invalidToken(this.name);
}
return value;
}
/**
* Returns the token by reading it from the token provider
*/
async getProviderToken(tokenId, value) {
const providerToken = await this.tokenProvider.read(tokenId, this.generateHash(value), this.tokenType);
if (!providerToken) {
throw AuthenticationException_1.AuthenticationException.invalidToken(this.name);
}
return providerToken;
}
/**
* Returns user from the user session id
*/
async getUserById(id) {
const authenticatable = await this.provider.findById(id);
if (!authenticatable.user) {
throw AuthenticationException_1.AuthenticationException.invalidToken(this.name);
}
return authenticatable;
}
/**
* Verify user credentials and perform login
*/
async attempt(uid, password, options) {
const user = await this.verifyCredentials(uid, password);
return this.login(user, options);
}
/**
* Login user using their id
*/
async loginViaId(id, options) {
const providerUser = await this.findById(id);
return this.login(providerUser.user, options);
}
/**
* Generate token for a user. It is merely an alias for `login`
*/
async generate(user, options) {
return this.login(user, options);
}
/**
* Login a user
*/
async login(user, options) {
/**
* Normalize options with defaults
*/
const { expiresIn, name, ...meta } = Object.assign({
name: 'Opaque Access Token',
}, options);
/**
* Since the login method is not exposed to the end user, we cannot expect
* them to instantiate and pass an instance of provider user, so we
* create one manually.
*/
const providerUser = await this.getUserForLogin(user, this.config.provider.identifierKey);
/**
* "getUserForLogin" raises exception when id is missing, so we can
* safely assume it is defined
*/
const id = providerUser.getId();
const token = this.generateTokenForPersistance(expiresIn);
/**
* Persist token to the database. Make sure that we are always
* passing the hash to the storage driver
*/
const providerToken = new ProviderToken_1.ProviderToken(name, token.hash, id, this.tokenType);
providerToken.expiresAt = token.expiresAt;
providerToken.meta = meta;
const tokenId = await this.tokenProvider.write(providerToken);
/**
* Construct a new API Token instance
*/
const apiToken = new OpaqueToken_1.OpaqueToken(name, `${helpers_1.base64.urlEncode(tokenId)}.${token.token}`, providerUser.user);
apiToken.tokenHash = token.hash;
apiToken.expiresAt = token.expiresAt;
apiToken.meta = meta || {};
/**
* Emit login event. It can be used to track user logins.
*/
this.emitter.emit('adonis:api:login', this.getLoginEventData(providerUser.user, apiToken));
/**
* Marking user as logged in
*/
this.markUserAsLoggedIn(providerUser.user);
this.token = providerToken;
return apiToken;
}
/**
* Authenticates the current HTTP request by checking for the bearer token
*/
async authenticate() {
/**
* Return early when authentication has already attempted for
* the current request
*/
if (this.authenticationAttempted) {
return this.user;
}
this.authenticationAttempted = true;
/**
* Ensure the "Authorization" header value exists
*/
const token = this.getBearerToken();
const { tokenId, value } = this.parsePublicToken(token);
/**
* Query token and user
*/
const providerToken = await this.getProviderToken(tokenId, value);
const providerUser = await this.getUserById(providerToken.userId);
this.markUserAsLoggedIn(providerUser.user, true);
this.token = providerToken;
this.emitter.emit('adonis:api:authenticate', this.getAuthenticateEventData(providerUser.user, this.token));
return providerUser.user;
}
/**
* Same as [[authenticate]] but returns a boolean over raising exceptions
*/
async check() {
try {
await this.authenticate();
}
catch (error) {
/**
* Throw error when it is not an instance of the authentication
*/
if (error instanceof AuthenticationException_1.AuthenticationException === false) {
throw error;
}
this.ctx.logger.trace(error, 'Authentication failure');
}
return this.isAuthenticated;
}
/**
* Alias for the logout method
*/
async revoke() {
return this.logout();
}
/**
* Logout by removing the token from the storage
*/
async logout() {
if (!this.authenticationAttempted) {
await this.check();
}
/**
* Clean up token from storage
*/
if (this.parsedToken) {
await this.tokenProvider.destroy(this.parsedToken.tokenId, this.tokenType);
}
this.markUserAsLoggedOut();
}
/**
* Serialize toJSON for JSON.stringify
*/
toJSON() {
return {
isLoggedIn: this.isLoggedIn,
isGuest: this.isGuest,
authenticationAttempted: this.authenticationAttempted,
isAuthenticated: this.isAuthenticated,
user: this.user,
};
}
}
exports.OATGuard = OATGuard;
+127
View File
@@ -0,0 +1,127 @@
/// <reference types="@adonisjs/events/build/adonis-typings" />
import { EmitterContract } from '@ioc:Adonis/Core/Event';
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
import { UserProviderContract, SessionGuardConfig, SessionGuardContract } from '@ioc:Adonis/Addons/Auth';
import { BaseGuard } from '../Base';
/**
* Session guard enables user login using sessions. Also it allows for
* setting remember me tokens for life long login
*/
export declare class SessionGuard extends BaseGuard<any> implements SessionGuardContract<any, any> {
private emitter;
private ctx;
constructor(name: string, config: SessionGuardConfig<any>, emitter: EmitterContract, provider: UserProviderContract<any>, ctx: HttpContextContract);
/**
* Number of years for the remember me token expiry
*/
private rememberMeTokenExpiry;
/**
* The name of the session key name
*/
get sessionKeyName(): string;
/**
* The name of the session key name
*/
get rememberMeKeyName(): string;
/**
* Returns the session object from the context.
*/
private getSession;
/**
* Set the user id inside the session. Also forces the session module
* to re-generate the session id
*/
private setSession;
/**
* Generate remember me token
*/
private generateRememberMeToken;
/**
* Sets the remember me cookie with the remember me token
*/
private setRememberMeCookie;
/**
* Clears the remember me cookie
*/
private clearRememberMeCookie;
/**
* Clears user session and remember me cookie
*/
private clearUserFromStorage;
/**
* Returns data packet for the login event. Arguments are
*
* - The mapping identifier
* - Logged in user
* - HTTP context
* - Remember me token (optional)
*/
private getLoginEventData;
/**
* Returns data packet for the authenticate event. Arguments are
*
* - The mapping identifier
* - Logged in user
* - HTTP context
* - A boolean to tell if logged in viaRemember or not
*/
private getAuthenticateEventData;
/**
* Returns the user id for the current HTTP request
*/
private getRequestSessionId;
/**
* Verifies the remember me token
*/
private verifyRememberMeToken;
/**
* Returns user from the user session id
*/
private getUserForSessionId;
/**
* Returns user for the remember me token
*/
private getUserForRememberMeToken;
/**
* Returns the remember me token of the user that is persisted
* inside the db. If not persisted, we create one and persist
* it
*/
private getPersistedRememberMeToken;
/**
* Verify user credentials and perform login
*/
attempt(uid: string, password: string, remember?: boolean): Promise<any>;
/**
* Login user using their id
*/
loginViaId(id: string | number, remember?: boolean): Promise<void>;
/**
* Login a user
*/
login(user: any, remember?: boolean): Promise<void>;
/**
* Authenticates the current HTTP request by checking for the user
* session.
*/
authenticate(): Promise<any>;
/**
* Same as [[authenticate]] but returns a boolean over raising exceptions
*/
check(): Promise<boolean>;
/**
* Logout by clearing session and cookies
*/
logout(recycleRememberToken?: boolean): Promise<void>;
/**
* Serialize toJSON for JSON.stringify
*/
toJSON(): {
isLoggedIn: boolean;
isGuest: boolean;
viaRemember: boolean;
authenticationAttempted: boolean;
isAuthenticated: boolean;
user: any;
};
}
+338
View File
@@ -0,0 +1,338 @@
"use strict";
/*
* @adonisjs/auth
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.SessionGuard = void 0;
const utils_1 = require("@poppinss/utils");
const helpers_1 = require("@poppinss/utils/build/helpers");
const Base_1 = require("../Base");
const AuthenticationException_1 = require("../../Exceptions/AuthenticationException");
/**
* Session guard enables user login using sessions. Also it allows for
* setting remember me tokens for life long login
*/
class SessionGuard extends Base_1.BaseGuard {
constructor(name, config, emitter, provider, ctx) {
super(name, config, provider);
this.emitter = emitter;
this.ctx = ctx;
/**
* Number of years for the remember me token expiry
*/
this.rememberMeTokenExpiry = '5y';
}
/**
* The name of the session key name
*/
get sessionKeyName() {
return `auth_${this.name}`;
}
/**
* The name of the session key name
*/
get rememberMeKeyName() {
return `remember_${this.name}`;
}
/**
* Returns the session object from the context.
*/
getSession() {
if (!this.ctx.session) {
throw new utils_1.Exception('"@adonisjs/session" is required to use the "session" auth driver');
}
return this.ctx.session;
}
/**
* Set the user id inside the session. Also forces the session module
* to re-generate the session id
*/
setSession(userId) {
this.getSession().put(this.sessionKeyName, userId);
this.getSession().regenerate();
}
/**
* Generate remember me token
*/
generateRememberMeToken() {
return helpers_1.string.generateRandom(20);
}
/**
* Sets the remember me cookie with the remember me token
*/
setRememberMeCookie(userId, token) {
const value = {
id: userId,
token: token,
};
this.ctx.response.encryptedCookie(this.rememberMeKeyName, value, {
maxAge: this.rememberMeTokenExpiry,
httpOnly: true,
});
}
/**
* Clears the remember me cookie
*/
clearRememberMeCookie() {
this.ctx.response.clearCookie(this.rememberMeKeyName);
}
/**
* Clears user session and remember me cookie
*/
clearUserFromStorage() {
this.getSession().forget(this.sessionKeyName);
this.clearRememberMeCookie();
}
/**
* Returns data packet for the login event. Arguments are
*
* - The mapping identifier
* - Logged in user
* - HTTP context
* - Remember me token (optional)
*/
getLoginEventData(user, token) {
return {
name: this.name,
ctx: this.ctx,
user,
token,
};
}
/**
* Returns data packet for the authenticate event. Arguments are
*
* - The mapping identifier
* - Logged in user
* - HTTP context
* - A boolean to tell if logged in viaRemember or not
*/
getAuthenticateEventData(user, viaRemember) {
return {
name: this.name,
ctx: this.ctx,
user,
viaRemember,
};
}
/**
* Returns the user id for the current HTTP request
*/
getRequestSessionId() {
return this.getSession().get(this.sessionKeyName);
}
/**
* Verifies the remember me token
*/
verifyRememberMeToken(rememberMeToken) {
if (!rememberMeToken || !rememberMeToken.id || !rememberMeToken.token) {
throw AuthenticationException_1.AuthenticationException.invalidSession(this.name);
}
}
/**
* Returns user from the user session id
*/
async getUserForSessionId(id) {
const authenticatable = await this.provider.findById(id);
if (!authenticatable.user) {
throw AuthenticationException_1.AuthenticationException.invalidSession(this.name);
}
return authenticatable;
}
/**
* Returns user for the remember me token
*/
async getUserForRememberMeToken(id, token) {
const authenticatable = await this.provider.findByRememberMeToken(id, token);
if (!authenticatable.user) {
throw AuthenticationException_1.AuthenticationException.invalidSession(this.name);
}
return authenticatable;
}
/**
* Returns the remember me token of the user that is persisted
* inside the db. If not persisted, we create one and persist
* it
*/
async getPersistedRememberMeToken(providerUser) {
/**
* Create and persist the user remember me token, when an existing one is missing
*/
if (!providerUser.getRememberMeToken()) {
this.ctx.logger.trace('generating fresh remember me token');
providerUser.setRememberMeToken(this.generateRememberMeToken());
await this.provider.updateRememberMeToken(providerUser);
}
return providerUser.getRememberMeToken();
}
/**
* Verify user credentials and perform login
*/
async attempt(uid, password, remember) {
const user = await this.verifyCredentials(uid, password);
await this.login(user, remember);
return user;
}
/**
* Login user using their id
*/
async loginViaId(id, remember) {
const providerUser = await this.findById(id);
await this.login(providerUser.user, remember);
return providerUser.user;
}
/**
* Login a user
*/
async login(user, remember) {
/**
* Since the login method is exposed to the end user, we cannot expect
* them to instantiate and return an instance of authenticatable, so
* we create one manually.
*/
const providerUser = await this.getUserForLogin(user, this.config.provider.identifierKey);
/**
* getUserForLogin raises exception when id is missing, so we can
* safely assume it is defined
*/
const id = providerUser.getId();
/**
* Set session
*/
this.setSession(id);
/**
* Set remember me token when enabled
*/
if (remember) {
const rememberMeToken = await this.getPersistedRememberMeToken(providerUser);
this.ctx.logger.trace('setting remember me cookie', { name: this.rememberMeKeyName });
this.setRememberMeCookie(id, rememberMeToken);
}
else {
/**
* Clear remember me cookie, which may have been set previously.
*/
this.clearRememberMeCookie();
}
/**
* Emit login event. It can be used to track user logins and their devices.
*/
this.emitter.emit('adonis:session:login', this.getLoginEventData(providerUser.user, providerUser.getRememberMeToken()));
this.markUserAsLoggedIn(providerUser.user);
return providerUser.user;
}
/**
* Authenticates the current HTTP request by checking for the user
* session.
*/
async authenticate() {
if (this.authenticationAttempted) {
return this.user;
}
this.authenticationAttempted = true;
const sessionId = this.getRequestSessionId();
/**
* If session id exists, then attempt to login the user using the
* session and return early
*/
if (sessionId) {
const providerUser = await this.getUserForSessionId(sessionId);
this.markUserAsLoggedIn(providerUser.user, true);
this.emitter.emit('adonis:session:authenticate', this.getAuthenticateEventData(providerUser.user, false));
return this.user;
}
/**
* Otherwise look for remember me token. Raise exception, if both remember
* me token and session id are missing.
*/
const rememberMeToken = this.ctx.request.encryptedCookie(this.rememberMeKeyName);
if (!rememberMeToken) {
throw AuthenticationException_1.AuthenticationException.invalidSession(this.name);
}
/**
* Ensure remember me token is valid after reading it from the cookie
*/
this.verifyRememberMeToken(rememberMeToken);
/**
* Attempt to locate the user for remember me token
*/
const providerUser = await this.getUserForRememberMeToken(rememberMeToken.id, rememberMeToken.token);
this.setSession(providerUser.getId());
this.setRememberMeCookie(rememberMeToken.id, rememberMeToken.token);
this.markUserAsLoggedIn(providerUser.user, true, true);
this.emitter.emit('adonis:session:authenticate', this.getAuthenticateEventData(providerUser.user, true));
return this.user;
}
/**
* Same as [[authenticate]] but returns a boolean over raising exceptions
*/
async check() {
try {
await this.authenticate();
}
catch (error) {
/**
* Throw error when it is not an instance of the authentication
*/
if (error instanceof AuthenticationException_1.AuthenticationException === false) {
throw error;
}
this.ctx.logger.trace(error, 'Authentication failure');
}
return this.isAuthenticated;
}
/**
* Logout by clearing session and cookies
*/
async logout(recycleRememberToken) {
/**
* Return early when not attempting to re-generate the remember me token
*/
if (!recycleRememberToken) {
this.clearUserFromStorage();
this.markUserAsLoggedOut();
return;
}
/**
* Attempt to authenticate the current request if not already authenticated. This
* will help us get an instance of the current user
*/
if (!this.authenticationAttempted) {
await this.check();
}
/**
* If authentication passed, then re-generate the remember me token
* for the current user.
*/
if (this.user) {
const providerUser = await this.provider.getUserFor(this.user);
this.ctx.logger.trace('re-generating remember me token');
providerUser.setRememberMeToken(this.generateRememberMeToken());
await this.provider.updateRememberMeToken(providerUser);
}
/**
* Logout user
*/
this.clearUserFromStorage();
this.markUserAsLoggedOut();
}
/**
* Serialize toJSON for JSON.stringify
*/
toJSON() {
return {
isLoggedIn: this.isLoggedIn,
isGuest: this.isGuest,
viaRemember: this.viaRemember,
authenticationAttempted: this.authenticationAttempted,
isAuthenticated: this.isAuthenticated,
user: this.user,
};
}
}
exports.SessionGuard = SessionGuard;
@@ -0,0 +1,43 @@
import { DatabaseContract, QueryClientContract } from '@ioc:Adonis/Lucid/Database';
import { TokenProviderContract, ProviderTokenContract, DatabaseTokenProviderConfig } from '@ioc:Adonis/Addons/Auth';
import { ProviderToken } from '../../Tokens/ProviderToken';
/**
* Database backend tokens provider
*/
export declare class TokenDatabaseProvider implements TokenProviderContract {
private config;
private db;
constructor(config: DatabaseTokenProviderConfig, db: DatabaseContract);
/**
* Custom connection or query client
*/
private connection?;
/**
* Returns the query client for database queries
*/
private getQueryClient;
/**
* The foreign key column
*/
private foreignKey;
/**
* Returns the builder query for a given token + type
*/
private getLookupQuery;
/**
* Define custom connection
*/
setConnection(connection: string | QueryClientContract): this;
/**
* Reads the token using the lookup token id
*/
read(tokenId: string, tokenHash: string, tokenType: string): Promise<ProviderTokenContract | null>;
/**
* Saves the token and returns the persisted token lookup id.
*/
write(token: ProviderToken): Promise<string>;
/**
* Removes a given token
*/
destroy(tokenId: string, tokenType: string): Promise<void>;
}
+126
View File
@@ -0,0 +1,126 @@
"use strict";
/*
* @adonisjs/auth
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.TokenDatabaseProvider = void 0;
const luxon_1 = require("luxon");
const helpers_1 = require("@poppinss/utils/build/helpers");
const ProviderToken_1 = require("../../Tokens/ProviderToken");
/**
* Database backend tokens provider
*/
class TokenDatabaseProvider {
constructor(config, db) {
this.config = config;
this.db = db;
/**
* The foreign key column
*/
this.foreignKey = this.config.foreignKey || 'user_id';
}
/**
* Returns the query client for database queries
*/
getQueryClient() {
if (!this.connection) {
return this.db.connection(this.config.connection);
}
return typeof this.connection === 'string'
? this.db.connection(this.connection)
: this.connection;
}
/**
* Returns the builder query for a given token + type
*/
getLookupQuery(tokenId, tokenType) {
return this.getQueryClient()
.from(this.config.table)
.where('id', tokenId)
.where('type', tokenType);
}
/**
* Define custom connection
*/
setConnection(connection) {
this.connection = connection;
return this;
}
/**
* Reads the token using the lookup token id
*/
async read(tokenId, tokenHash, tokenType) {
const client = this.getQueryClient();
/**
* Find token using id
*/
const tokenRow = await this.getLookupQuery(tokenId, tokenType).first();
if (!tokenRow || !tokenRow.token) {
return null;
}
/**
* Ensure hash of the user provided value is same as the one inside
* the database
*/
if (!(0, helpers_1.safeEqual)(tokenRow.token, tokenHash)) {
return null;
}
const { name, [this.foreignKey]: userId, token: value, expires_at: expiresAt, type, ...meta } = tokenRow;
let normalizedExpiryDate;
/**
* Parse dialect date to an instance of Luxon
*/
if (expiresAt instanceof Date) {
normalizedExpiryDate = luxon_1.DateTime.fromJSDate(expiresAt);
}
else if (expiresAt && typeof expiresAt === 'string') {
normalizedExpiryDate = luxon_1.DateTime.fromFormat(expiresAt, client.dialect.dateTimeFormat);
}
else if (expiresAt && typeof expiresAt === 'number') {
normalizedExpiryDate = luxon_1.DateTime.fromMillis(expiresAt);
}
/**
* Ensure token isn't expired
*/
if (normalizedExpiryDate &&
normalizedExpiryDate.diff(luxon_1.DateTime.local(), 'milliseconds').milliseconds <= 0) {
return null;
}
const token = new ProviderToken_1.ProviderToken(name, value, userId, type);
token.expiresAt = expiresAt;
token.meta = meta;
return token;
}
/**
* Saves the token and returns the persisted token lookup id.
*/
async write(token) {
const client = this.getQueryClient();
/**
* Payload to save to the database
*/
const payload = {
[this.foreignKey]: token.userId,
name: token.name,
token: token.tokenHash,
type: token.type,
expires_at: token.expiresAt ? token.expiresAt.toFormat(client.dialect.dateTimeFormat) : null,
created_at: luxon_1.DateTime.local().toFormat(client.dialect.dateTimeFormat),
...token.meta,
};
const [row] = await client.table(this.config.table).insert(payload).returning('id');
return String(typeof row === 'number' ? row : row.id);
}
/**
* Removes a given token
*/
async destroy(tokenId, tokenType) {
await this.getLookupQuery(tokenId, tokenType).delete();
}
}
exports.TokenDatabaseProvider = TokenDatabaseProvider;
+44
View File
@@ -0,0 +1,44 @@
import { RedisManagerContract, RedisConnectionContract, RedisClusterConnectionContract } from '@ioc:Adonis/Addons/Redis';
import { TokenProviderContract, ProviderTokenContract, RedisTokenProviderConfig } from '@ioc:Adonis/Addons/Auth';
import { ProviderToken } from '../../Tokens/ProviderToken';
/**
* Redis backed tokens provider.
*/
export declare class TokenRedisProvider implements TokenProviderContract {
private config;
private redis;
constructor(config: RedisTokenProviderConfig, redis: RedisManagerContract);
/**
* Custom connection or query client
*/
private connection?;
/**
* Returns the singleton instance of the redis connection
*/
private getRedisConnection;
/**
* The foreign key column
*/
private foreignKey;
/**
* Parse the stringified redis token value to an object
*/
private parseToken;
/**
* Define custom connection
*/
setConnection(connection: string | RedisConnectionContract | RedisClusterConnectionContract): this;
/**
* Reads the token using the lookup token id
*/
read(tokenId: string, tokenHash: string, tokenType: string): Promise<ProviderTokenContract | null>;
/**
* Saves the token and returns the persisted token lookup id, which
* is a cuid.
*/
write(token: ProviderToken): Promise<string>;
/**
* Removes a given token
*/
destroy(tokenId: string, tokenType: string): Promise<void>;
}
+129
View File
@@ -0,0 +1,129 @@
"use strict";
/*
* @adonisjs/auth
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.TokenRedisProvider = void 0;
const utils_1 = require("@poppinss/utils");
const helpers_1 = require("@poppinss/utils/build/helpers");
const ProviderToken_1 = require("../../Tokens/ProviderToken");
/**
* Redis backed tokens provider.
*/
class TokenRedisProvider {
constructor(config, redis) {
this.config = config;
this.redis = redis;
/**
* The foreign key column
*/
this.foreignKey = this.config.foreignKey || 'user_id';
}
/**
* Returns the singleton instance of the redis connection
*/
getRedisConnection() {
/**
* Use custom connection if defined
*/
if (this.connection) {
return typeof this.connection === 'string'
? this.redis.connection(this.connection)
: this.connection;
}
/**
* Config must have a connection defined
*/
if (!this.config.redisConnection) {
throw new utils_1.Exception('Missing "redisConnection" property for auth redis provider inside "config/auth" file', 500, 'E_INVALID_AUTH_REDIS_CONFIG');
}
return this.redis.connection(this.config.redisConnection);
}
/**
* Parse the stringified redis token value to an object
*/
parseToken(token) {
if (!token) {
return null;
}
try {
const tokenRow = JSON.parse(token);
if (!tokenRow.token || !tokenRow.name || !tokenRow[this.foreignKey]) {
return null;
}
return tokenRow;
}
catch {
return null;
}
}
/**
* Define custom connection
*/
setConnection(connection) {
this.connection = connection;
return this;
}
/**
* Reads the token using the lookup token id
*/
async read(tokenId, tokenHash, tokenType) {
/**
* Find token using id
*/
const tokenRow = this.parseToken(await this.getRedisConnection().get(`${tokenType}:${tokenId}`));
if (!tokenRow) {
return null;
}
/**
* Ensure hash of the user provided value is same as the one inside
* the database
*/
if (!(0, helpers_1.safeEqual)(tokenRow.token, tokenHash)) {
return null;
}
const { name, [this.foreignKey]: userId, token: value, ...meta } = tokenRow;
const token = new ProviderToken_1.ProviderToken(name, value, userId, tokenType);
token.meta = meta;
return token;
}
/**
* Saves the token and returns the persisted token lookup id, which
* is a cuid.
*/
async write(token) {
/**
* Payload to save to the database
*/
const payload = {
[this.foreignKey]: token.userId,
name: token.name,
token: token.tokenHash,
...token.meta,
};
const ttl = token.expiresAt ? Math.ceil(token.expiresAt.diffNow('seconds').seconds) : 0;
const tokenId = (0, helpers_1.cuid)();
if (token.expiresAt && ttl <= 0) {
throw new utils_1.Exception('The expiry date/time should be in the future', 500, 'E_INVALID_TOKEN_EXPIRY');
}
if (token.expiresAt) {
await this.getRedisConnection().setex(`${token.type}:${tokenId}`, ttl, JSON.stringify(payload));
}
else {
await this.getRedisConnection().set(`${token.type}:${tokenId}`, JSON.stringify(payload));
}
return tokenId;
}
/**
* Removes a given token
*/
async destroy(tokenId, tokenType) {
await this.getRedisConnection().del(`${tokenType}:${tokenId}`);
}
}
exports.TokenRedisProvider = TokenRedisProvider;
+46
View File
@@ -0,0 +1,46 @@
import { DateTime } from 'luxon';
import { OpaqueTokenContract } from '@ioc:Adonis/Addons/Auth';
/**
* Opaque token represents a persisted token generated for a given user
*
* Calling `opaqueToken.toJSON()` will give you an object, that you can send back
* as response to share the token with the client.
*/
export declare class OpaqueToken implements OpaqueTokenContract<any> {
name: string;
token: string;
user: any;
/**
* The type of the token. Always set to bearer
*/
type: "bearer";
/**
* The datetime in which the token will expire
*/
expiresAt?: DateTime;
/**
* Time left until token gets expired
*/
expiresIn?: number;
/**
* Any meta data attached to the token
*/
meta: any;
/**
* Hash of the token saved inside the database. Make sure to never share
* this with the client
*/
tokenHash: string;
constructor(name: string, // Name associated with the token
token: string, // The raw token value. Only available for the first time
user: any);
/**
* Shareable version of the token
*/
toJSON(): {
expires_in?: number | undefined;
expires_at?: string | undefined;
type: "bearer";
token: string;
};
}
+43
View File
@@ -0,0 +1,43 @@
"use strict";
/*
* @adonisjs/auth
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.OpaqueToken = void 0;
/**
* Opaque token represents a persisted token generated for a given user
*
* Calling `opaqueToken.toJSON()` will give you an object, that you can send back
* as response to share the token with the client.
*/
class OpaqueToken {
constructor(name, // Name associated with the token
token, // The raw token value. Only available for the first time
user // The user for which the token is generated
) {
this.name = name;
this.token = token;
this.user = user;
/**
* The type of the token. Always set to bearer
*/
this.type = 'bearer';
}
/**
* Shareable version of the token
*/
toJSON() {
return {
type: this.type,
token: this.token,
...(this.expiresAt ? { expires_at: this.expiresAt.toISO() || undefined } : {}),
...(this.expiresIn ? { expires_in: this.expiresIn } : {}),
};
}
}
exports.OpaqueToken = OpaqueToken;
+23
View File
@@ -0,0 +1,23 @@
import { DateTime } from 'luxon';
import { ProviderTokenContract } from '@ioc:Adonis/Addons/Auth';
/**
* Token returned and accepted by the token providers
*/
export declare class ProviderToken implements ProviderTokenContract {
name: string;
tokenHash: string;
userId: string | number;
type: string;
/**
* Expiry date
*/
expiresAt?: DateTime;
/**
* All other token details
*/
meta?: any;
constructor(name: string, // Name associated with the token
tokenHash: string, // The hash to persist
userId: string | number, // The user for which the token is generated
type: string);
}
+27
View File
@@ -0,0 +1,27 @@
"use strict";
/*
* @adonisjs/auth
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProviderToken = void 0;
/**
* Token returned and accepted by the token providers
*/
class ProviderToken {
constructor(name, // Name associated with the token
tokenHash, // The hash to persist
userId, // The user for which the token is generated
type // The type of the token.
) {
this.name = name;
this.tokenHash = tokenHash;
this.userId = userId;
this.type = type;
}
}
exports.ProviderToken = ProviderToken;
+28
View File
@@ -0,0 +1,28 @@
/// <reference types="@adonisjs/hash/build/adonis-typings" />
import type { HashContract } from '@ioc:Adonis/Core/Hash';
import type { ProviderUserContract, DatabaseProviderRow, DatabaseProviderConfig } from '@ioc:Adonis/Addons/Auth';
/**
* Database user works a bridge between the provider and the guard
*/
export declare class DatabaseUser implements ProviderUserContract<DatabaseProviderRow> {
user: DatabaseProviderRow | null;
private config;
private hash;
constructor(user: DatabaseProviderRow | null, config: DatabaseProviderConfig, hash: HashContract);
/**
* Returns the value of the user id
*/
getId(): any;
/**
* Verifies the user password
*/
verifyPassword(plainPassword: string): Promise<boolean>;
/**
* Returns the user remember me token or null
*/
getRememberMeToken(): string | null;
/**
* Updates user remember me token
*/
setRememberMeToken(token: string): void;
}
+74
View File
@@ -0,0 +1,74 @@
"use strict";
/*
* @adonisjs/auth
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DatabaseUser = void 0;
const utils_1 = require("@poppinss/utils");
const standalone_1 = require("@adonisjs/core/build/standalone");
/**
* Database user works a bridge between the provider and the guard
*/
let DatabaseUser = class DatabaseUser {
constructor(user, config, hash) {
this.user = user;
this.config = config;
this.hash = hash;
}
/**
* Returns the value of the user id
*/
getId() {
return this.user ? this.user[this.config.identifierKey] : null;
}
/**
* Verifies the user password
*/
async verifyPassword(plainPassword) {
if (!this.user) {
throw new utils_1.Exception('Cannot "verifyPassword" for non-existing user');
}
/**
* Ensure user has password
*/
if (!this.user.password) {
throw new utils_1.Exception('Auth user object must have a password in order to call "verifyPassword"');
}
const hasher = this.config.hashDriver ? this.hash.use(this.config.hashDriver) : this.hash;
return hasher.verify(this.user.password, plainPassword);
}
/**
* Returns the user remember me token or null
*/
getRememberMeToken() {
return this.user ? this.user.remember_me_token || null : null;
}
/**
* Updates user remember me token
*/
setRememberMeToken(token) {
if (!this.user) {
throw new utils_1.Exception('Cannot set "rememberMeToken" on non-existing user');
}
this.user.remember_me_token = token;
}
};
DatabaseUser = __decorate([
(0, standalone_1.inject)([null, null, 'Adonis/Core/Hash']),
__metadata("design:paramtypes", [Object, Object, Object])
], DatabaseUser);
exports.DatabaseUser = DatabaseUser;
@@ -0,0 +1,75 @@
/// <reference types="@adonisjs/application/build/adonis-typings/application" />
import { ApplicationContract } from '@ioc:Adonis/Core/Application';
import { DatabaseContract, QueryClientContract } from '@ioc:Adonis/Lucid/Database';
import { DatabaseProviderRow, ProviderUserContract, DatabaseProviderConfig, DatabaseProviderContract } from '@ioc:Adonis/Addons/Auth';
/**
* Database provider to lookup users inside the database
*/
export declare class DatabaseProvider implements DatabaseProviderContract<DatabaseProviderRow> {
private application;
private config;
private db;
/**
* Hooks reference
*/
private hooks;
/**
* Custom connection or query client
*/
private connection?;
/**
* Name of the remember_me_token column
*/
private rememberMeColumn;
constructor(application: ApplicationContract, config: DatabaseProviderConfig, db: DatabaseContract);
/**
* Returns the query client for invoking queries
*/
private getQueryClient;
/**
* Returns the query builder instance for the users table
*/
private getUserQueryBuilder;
/**
* Ensure "user.id" is always present
*/
private ensureUserHasId;
/**
* Executes the query to find the user, calls the registered hooks
* and wraps the result inside [[ProviderUserContract]]
*/
private findUser;
/**
* Returns an instance of provider user
*/
getUserFor(user: any): Promise<ProviderUserContract<DatabaseProviderRow>>;
/**
* Define custom connection
*/
setConnection(connection: string | QueryClientContract): this;
/**
* Define before hooks. Check interface for exact type information
*/
before(event: 'findUser', callback: (query: any) => Promise<void>): this;
/**
* Define after hooks. Check interface for exact type information
*/
after(event: 'findUser', callback: (...args: any[]) => Promise<void>): this;
/**
* Returns the user row using the primary key
*/
findById(id: string | number): Promise<ProviderUserContract<DatabaseProviderRow>>;
/**
* Returns a user from their remember me token
*/
findByRememberMeToken(id: number | string, token: string): Promise<ProviderUserContract<DatabaseProviderRow>>;
/**
* Returns the user row by searching the uidValue against
* their defined uids.
*/
findByUid(uidValue: string): Promise<ProviderUserContract<DatabaseProviderRow>>;
/**
* Updates the user remember me token
*/
updateRememberMeToken(user: ProviderUserContract<DatabaseProviderRow>): Promise<void>;
}
+141
View File
@@ -0,0 +1,141 @@
"use strict";
/*
* @adonisjs/auth
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.DatabaseProvider = void 0;
const hooks_1 = require("@poppinss/hooks");
const utils_1 = require("@poppinss/utils");
const User_1 = require("./User");
/**
* Database provider to lookup users inside the database
*/
class DatabaseProvider {
constructor(application, config, db) {
this.application = application;
this.config = config;
this.db = db;
/**
* Hooks reference
*/
this.hooks = new hooks_1.Hooks();
/**
* Name of the remember_me_token column
*/
this.rememberMeColumn = 'remember_me_token';
}
/**
* Returns the query client for invoking queries
*/
getQueryClient() {
if (!this.connection) {
return this.db.connection(this.config.connection);
}
return typeof this.connection === 'string'
? this.db.connection(this.connection)
: this.connection;
}
/**
* Returns the query builder instance for the users table
*/
getUserQueryBuilder() {
return this.getQueryClient().from(this.config.usersTable);
}
/**
* Ensure "user.id" is always present
*/
ensureUserHasId(user) {
/**
* Ignore when user is null
*/
if (!user) {
return;
}
if (!user[this.config.identifierKey]) {
throw new utils_1.Exception(`Auth database provider expects "${this.config.usersTable}.${this.config.identifierKey}" to always exist`);
}
}
/**
* Executes the query to find the user, calls the registered hooks
* and wraps the result inside [[ProviderUserContract]]
*/
async findUser(query) {
await this.hooks.exec('before', 'findUser', query);
const user = await query.first();
if (user) {
await this.hooks.exec('after', 'findUser', user);
}
return this.getUserFor(user);
}
/**
* Returns an instance of provider user
*/
async getUserFor(user) {
this.ensureUserHasId(user);
const UserBuilder = this.config.user ? (0, utils_1.esmResolver)(await this.config.user()) : User_1.DatabaseUser;
return this.application.container.makeAsync(UserBuilder, [user, this.config]);
}
/**
* Define custom connection
*/
setConnection(connection) {
this.connection = connection;
return this;
}
/**
* Define before hooks. Check interface for exact type information
*/
before(event, callback) {
this.hooks.add('before', event, callback);
return this;
}
/**
* Define after hooks. Check interface for exact type information
*/
after(event, callback) {
this.hooks.add('after', event, callback);
return this;
}
/**
* Returns the user row using the primary key
*/
async findById(id) {
const query = this.getUserQueryBuilder();
return this.findUser(query.where(this.config.identifierKey, id));
}
/**
* Returns a user from their remember me token
*/
async findByRememberMeToken(id, token) {
const query = this.getUserQueryBuilder()
.where(this.rememberMeColumn, token)
.where(this.config.identifierKey, id);
return this.findUser(query);
}
/**
* Returns the user row by searching the uidValue against
* their defined uids.
*/
async findByUid(uidValue) {
const query = this.getUserQueryBuilder();
this.config.uids.forEach((uid) => query.orWhere(uid, uidValue));
return this.findUser(query);
}
/**
* Updates the user remember me token
*/
async updateRememberMeToken(user) {
this.ensureUserHasId(user);
await this.getUserQueryBuilder()
.where(this.config.identifierKey, user[this.config.identifierKey])
.update({
remember_me_token: user.getRememberMeToken(),
});
}
}
exports.DatabaseProvider = DatabaseProvider;
+28
View File
@@ -0,0 +1,28 @@
/// <reference types="@adonisjs/hash/build/adonis-typings" />
import type { HashContract } from '@ioc:Adonis/Core/Hash';
import type { LucidProviderModel, ProviderUserContract, LucidProviderConfig } from '@ioc:Adonis/Addons/Auth';
/**
* Lucid works works a bridge between the provider and the guard
*/
export declare class LucidUser<User extends LucidProviderModel> implements ProviderUserContract<InstanceType<User>> {
user: InstanceType<User> | null;
private config;
private hash;
constructor(user: InstanceType<User> | null, config: LucidProviderConfig<User>, hash: HashContract);
/**
* Returns the value of the user id
*/
getId(): any;
/**
* Verifies the user password
*/
verifyPassword(plainPassword: string): Promise<boolean>;
/**
* Returns the user remember me token or null
*/
getRememberMeToken(): string | null;
/**
* Updates user remember me token
*/
setRememberMeToken(token: string): void;
}
+74
View File
@@ -0,0 +1,74 @@
"use strict";
/*
* @adonisjs/auth
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.LucidUser = void 0;
const utils_1 = require("@poppinss/utils");
const standalone_1 = require("@adonisjs/core/build/standalone");
/**
* Lucid works works a bridge between the provider and the guard
*/
let LucidUser = class LucidUser {
constructor(user, config, hash) {
this.user = user;
this.config = config;
this.hash = hash;
}
/**
* Returns the value of the user id
*/
getId() {
return this.user ? this.user[this.config.identifierKey] : null;
}
/**
* Verifies the user password
*/
async verifyPassword(plainPassword) {
if (!this.user) {
throw new utils_1.Exception('Cannot "verifyPassword" for non-existing user');
}
/**
* Ensure user has password
*/
if (!this.user.password) {
throw new utils_1.Exception('Auth user object must have a password in order to call "verifyPassword"');
}
const hasher = this.config.hashDriver ? this.hash.use(this.config.hashDriver) : this.hash;
return hasher.verify(this.user.password, plainPassword);
}
/**
* Returns the user remember me token or null
*/
getRememberMeToken() {
return this.user ? this.user.rememberMeToken || null : null;
}
/**
* Updates user remember me token
*/
setRememberMeToken(token) {
if (!this.user) {
throw new utils_1.Exception('Cannot set "rememberMeToken" on non-existing user');
}
this.user.rememberMeToken = token;
}
};
LucidUser = __decorate([
(0, standalone_1.inject)([null, null, 'Adonis/Core/Hash']),
__metadata("design:paramtypes", [void 0, Object, Object])
], LucidUser);
exports.LucidUser = LucidUser;
+72
View File
@@ -0,0 +1,72 @@
/// <reference types="@adonisjs/application/build/adonis-typings/application" />
import { ApplicationContract } from '@ioc:Adonis/Core/Application';
import { QueryClientContract } from '@ioc:Adonis/Lucid/Database';
import { LucidProviderModel, LucidProviderConfig, ProviderUserContract, LucidProviderContract } from '@ioc:Adonis/Addons/Auth';
/**
* Lucid provider uses Lucid models to lookup a users
*/
export declare class LucidProvider implements LucidProviderContract<LucidProviderModel> {
private application;
private config;
/**
* Hooks reference
*/
private hooks;
/**
* Custom connection or query client
*/
private connection?;
constructor(application: ApplicationContract, config: LucidProviderConfig<LucidProviderModel>);
/**
* The models options for constructing a query
*/
private getModelOptions;
/**
* Returns the auth model
*/
private getModel;
/**
* Returns query instance for the user model
*/
private getModelQuery;
/**
* Executes the query to find the user, calls the registered hooks
* and wraps the result inside [[ProviderUserContract]]
*/
private findUser;
/**
* Returns an instance of the [[ProviderUser]] by wrapping lucid model
* inside it
*/
getUserFor(user: InstanceType<LucidProviderModel> | null): Promise<any>;
/**
* Define custom connection
*/
setConnection(connection: string | QueryClientContract): this;
/**
* Define before hooks. Check interface for exact type information
*/
before(event: 'findUser', callback: (query: any) => Promise<void>): this;
/**
* Define after hooks. Check interface for exact type information
*/
after(event: 'findUser', callback: (...args: any[]) => Promise<void>): this;
/**
* Returns a user instance using the primary key value
*/
findById(id: string | number): Promise<any>;
/**
* Returns a user instance using a specific token type and value
*/
findByRememberMeToken(id: string | number, value: string): Promise<any>;
/**
* Returns the user instance by searching the uidValue against
* their defined uids.
*/
findByUid(uidValue: string): Promise<any>;
/**
* Updates the user remember me token. The guard must called `setRememberMeToken`
* before invoking this method.
*/
updateRememberMeToken(providerUser: ProviderUserContract<InstanceType<LucidProviderModel>>): Promise<void>;
}
+146
View File
@@ -0,0 +1,146 @@
"use strict";
/*
* @adonisjs/auth
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.LucidProvider = void 0;
const hooks_1 = require("@poppinss/hooks");
const utils_1 = require("@poppinss/utils");
const User_1 = require("./User");
/**
* Lucid provider uses Lucid models to lookup a users
*/
class LucidProvider {
constructor(application, config) {
this.application = application;
this.config = config;
/**
* Hooks reference
*/
this.hooks = new hooks_1.Hooks();
}
/**
* The models options for constructing a query
*/
getModelOptions() {
if (typeof this.connection === 'string') {
return { connection: this.connection };
}
if (this.connection) {
return { client: this.connection };
}
return this.config.connection ? { connection: this.config.connection } : {};
}
/**
* Returns the auth model
*/
async getModel() {
const model = await this.config.model();
return (0, utils_1.esmResolver)(model);
}
/**
* Returns query instance for the user model
*/
async getModelQuery(model) {
model = model || (await this.getModel());
return {
query: model.query(this.getModelOptions()),
};
}
/**
* Executes the query to find the user, calls the registered hooks
* and wraps the result inside [[ProviderUserContract]]
*/
async findUser(query) {
await this.hooks.exec('before', 'findUser', query);
const user = await query.first();
if (user) {
await this.hooks.exec('after', 'findUser', user);
}
return this.getUserFor(user);
}
/**
* Returns an instance of the [[ProviderUser]] by wrapping lucid model
* inside it
*/
async getUserFor(user) {
const UserBuilder = this.config.user ? (0, utils_1.esmResolver)(await this.config.user()) : User_1.LucidUser;
return this.application.container.makeAsync(UserBuilder, [user, this.config]);
}
/**
* Define custom connection
*/
setConnection(connection) {
this.connection = connection;
return this;
}
/**
* Define before hooks. Check interface for exact type information
*/
before(event, callback) {
this.hooks.add('before', event, callback);
return this;
}
/**
* Define after hooks. Check interface for exact type information
*/
after(event, callback) {
this.hooks.add('after', event, callback);
return this;
}
/**
* Returns a user instance using the primary key value
*/
async findById(id) {
const { query } = await this.getModelQuery();
return this.findUser(query.where(this.config.identifierKey, id));
}
/**
* Returns a user instance using a specific token type and value
*/
async findByRememberMeToken(id, value) {
const { query } = await this.getModelQuery();
return this.findUser(query.where(this.config.identifierKey, id).where('rememberMeToken', value));
}
/**
* Returns the user instance by searching the uidValue against
* their defined uids.
*/
async findByUid(uidValue) {
const model = await this.getModel();
/**
* Use custom function on the model. This time, we do not emit
* an event, since the user custom lookup may not even
* run a query at all.
*/
if (typeof model.findForAuth === 'function') {
const user = await model.findForAuth(this.config.uids, uidValue);
return this.getUserFor(user);
}
/**
* Lookup by running a custom query.
*/
const { query } = await this.getModelQuery();
this.config.uids.forEach((uid) => query.orWhere(uid, uidValue));
return this.findUser(query);
}
/**
* Updates the user remember me token. The guard must called `setRememberMeToken`
* before invoking this method.
*/
async updateRememberMeToken(providerUser) {
/**
* Extra check to find malformed guards
*/
if (!providerUser.user.$dirty.rememberMeToken) {
throw new Error('The guard must called "setRememberMeToken" before calling "updateRememberMeToken" on the Lucid provider');
}
await providerUser.user.save();
}
}
exports.LucidProvider = LucidProvider;
+1
View File
@@ -0,0 +1 @@
export { AuthenticationException } from './src/Exceptions/AuthenticationException';
+13
View File
@@ -0,0 +1,13 @@
"use strict";
/*
* @adonisjs/auth
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.AuthenticationException = void 0;
var AuthenticationException_1 = require("./src/Exceptions/AuthenticationException");
Object.defineProperty(exports, "AuthenticationException", { enumerable: true, get: function () { return AuthenticationException_1.AuthenticationException; } });
+34
View File
@@ -0,0 +1,34 @@
/**
* Config source: https://git.io/JY0mp
*
* Feel free to let us know via PR, if you find something broken in this config
* file.
*/
import type { AuthConfig } from '@ioc:Adonis/Addons/Auth'
/*
|--------------------------------------------------------------------------
| Authentication Mapping
|--------------------------------------------------------------------------
|
| List of available authentication mapping. You must first define them
| inside the `contracts/auth.ts` file before mentioning them here.
|
*/
const authConfig: AuthConfig = {
guard: '{{ guards.0 }}',
guards: {
{{#hasGuard.web}}
{{> web_guard}}
{{/hasGuard.web}}
{{#hasGuard.api}}
{{> api_guard}}
{{/hasGuard.api}}
{{#hasGuard.basic}}
{{> basic_guard}}
{{/hasGuard.basic}}
},
}
export default authConfig
@@ -0,0 +1,22 @@
/*
|--------------------------------------------------------------------------
| OAT Guard
|--------------------------------------------------------------------------
|
| OAT (Opaque access tokens) guard uses database backed tokens to authenticate
| HTTP request. This guard DOES NOT rely on sessions or cookies and uses
| Authorization header value for authentication.
|
| Use this guard to authenticate mobile apps or web clients that cannot rely
| on cookies/sessions.
|
*/
api: {
driver: 'oat',
{{> token_provider}}
provider: {
{{> provider}}
},
},
@@ -0,0 +1,19 @@
/*
|--------------------------------------------------------------------------
| Basic Auth Guard
|--------------------------------------------------------------------------
|
| Uses Basic auth to authenticate an HTTP request. There is no concept of
| "login" and "logout" with basic auth. You just authenticate the requests
| using a middleware and browser will prompt the user to enter their login
| details
|
*/
basic: {
driver: 'basic',
realm: 'Login',
provider: {
{{> provider}}
},
},
@@ -0,0 +1,19 @@
/*
|--------------------------------------------------------------------------
| Tokens provider
|--------------------------------------------------------------------------
|
| Uses SQL database for managing tokens. Use the "database" driver, when
| tokens are the secondary mode of authentication.
| For example: The Github personal tokens
|
| The foreignKey column is used to make the relationship between the user
| and the token. You are free to use any column name here.
|
*/
tokenProvider: {
type: 'api',
driver: 'database',
table: 'api_tokens',
foreignKey: 'user_id',
},
@@ -0,0 +1,22 @@
/*
|--------------------------------------------------------------------------
| Redis provider for managing tokens
|--------------------------------------------------------------------------
|
| Uses Redis for managing tokens. We recommend using the "redis" driver
| over the "database" driver when the tokens based auth is the
| primary authentication mode.
|
| Redis ensure that all the expired tokens gets cleaned up automatically.
| Whereas with SQL, you have to cleanup expired tokens manually.
|
| The foreignKey column is used to make the relationship between the user
| and the token. You are free to use any column name here.
|
*/
tokenProvider: {
type: 'api',
driver: 'redis',
redisConnection: 'local',
foreignKey: 'user_id',
},
@@ -0,0 +1,43 @@
/*
|--------------------------------------------------------------------------
| Driver
|--------------------------------------------------------------------------
|
| Name of the driver
|
*/
driver: 'database',
/*
|--------------------------------------------------------------------------
| Identifier key
|--------------------------------------------------------------------------
|
| The identifier key is the unique key inside the defined database table.
| In most cases specifying the primary key is the right choice.
|
*/
identifierKey: 'id',
/*
|--------------------------------------------------------------------------
| Uids
|--------------------------------------------------------------------------
|
| Uids are used to search a user against one of the mentioned columns. During
| login, the auth module will search the user mentioned value against one
| of the mentioned columns to find their user record.
|
*/
uids: ['email'],
/*
|--------------------------------------------------------------------------
| Database table
|--------------------------------------------------------------------------
|
| The database table to query. Make sure the database table has a `password`
| field and `remember_me_token` column.
|
*/
usersTable: '{{ usersTableName }}',
@@ -0,0 +1,45 @@
/*
|--------------------------------------------------------------------------
| Driver
|--------------------------------------------------------------------------
|
| Name of the driver
|
*/
driver: 'lucid',
/*
|--------------------------------------------------------------------------
| Identifier key
|--------------------------------------------------------------------------
|
| The identifier key is the unique key on the model. In most cases specifying
| the primary key is the right choice.
|
*/
identifierKey: 'id',
/*
|--------------------------------------------------------------------------
| Uids
|--------------------------------------------------------------------------
|
| Uids are used to search a user against one of the mentioned columns. During
| login, the auth module will search the user mentioned value against one
| of the mentioned columns to find their user record.
|
*/
uids: ['email'],
/*
|--------------------------------------------------------------------------
| Model
|--------------------------------------------------------------------------
|
| The model to use for fetching or finding users. The model is imported
| lazily since the config files are read way earlier in the lifecycle
| of booting the app and the models may not be in a usable state at
| that time.
|
*/
model: () => import('{{{ modelNamespace }}}'),
@@ -0,0 +1,17 @@
/*
|--------------------------------------------------------------------------
| Web Guard
|--------------------------------------------------------------------------
|
| Web guard uses classic old school sessions for authenticating users.
| If you are building a standard web application, it is recommended to
| use web guard with session driver
|
*/
web: {
driver: 'session',
provider: {
{{> provider}}
},
},
+55
View File
@@ -0,0 +1,55 @@
/**
* Contract source: https://git.io/JOdz5
*
* Feel free to let us know via PR, if you find something broken in this
* file.
*/
{{#modelNamespace}}
import {{ modelName }} from '{{{ modelNamespace }}}'
{{/modelNamespace}}
declare module '@ioc:Adonis/Addons/Auth' {
/*
|--------------------------------------------------------------------------
| Providers
|--------------------------------------------------------------------------
|
| The providers are used to fetch users. The Auth module comes pre-bundled
| with two providers that are `Lucid` and `Database`. Both uses database
| to fetch user details.
|
| You can also create and register your own custom providers.
|
*/
interface ProvidersList {
{{> provider}}
}
/*
|--------------------------------------------------------------------------
| Guards
|--------------------------------------------------------------------------
|
| The guards are used for authenticating users using different drivers.
| The auth module comes with 3 different guards.
|
| - SessionGuardContract
| - BasicAuthGuardContract
| - OATGuardContract ( Opaque access token )
|
| Every guard needs a provider for looking up users from the database.
|
*/
interface GuardsList {
{{#hasGuard.web}}
{{> web_guard}}
{{/hasGuard.web}}
{{#hasGuard.api}}
{{> api_guard}}
{{/hasGuard.api}}
{{#hasGuard.basic}}
{{> basic_guard}}
{{/hasGuard.basic}}
}
}
@@ -0,0 +1,14 @@
/*
|--------------------------------------------------------------------------
| OAT Guard
|--------------------------------------------------------------------------
|
| OAT, stands for (Opaque access tokens) guard uses database backed tokens
| to authenticate requests.
|
*/
api: {
implementation: OATGuardContract<'user', 'api'>
config: OATGuardConfig<'user'>
client: OATClientContract<'user'>
}
@@ -0,0 +1,14 @@
/*
|--------------------------------------------------------------------------
| Basic Auth Guard
|--------------------------------------------------------------------------
|
| The basic guard uses basic auth for maintaining user login state. It uses
| the `user` provider for fetching user details.
|
*/
basic: {
implementation: BasicAuthGuardContract<'user', 'basic'>
config: BasicAuthGuardConfig<'user'>
client: BasicAuthClientContract<'user'>
}
@@ -0,0 +1,16 @@
/*
|--------------------------------------------------------------------------
| User Provider
|--------------------------------------------------------------------------
|
| The following provider directlly uses Database query builder for fetching
| user details from the database for authentication.
|
| You can create multiple providers using the same underlying driver with
| different database tables.
|
*/
user: {
implementation: DatabaseProviderContract<DatabaseProviderRow>
config: DatabaseProviderConfig
}
@@ -0,0 +1,16 @@
/*
|--------------------------------------------------------------------------
| User Provider
|--------------------------------------------------------------------------
|
| The following provider uses Lucid models as a driver for fetching user
| details from the database for authentication.
|
| You can create multiple providers using the same underlying driver with
| different Lucid models.
|
*/
user: {
implementation: LucidProviderContract<typeof {{ modelName }}>
config: LucidProviderConfig<typeof {{ modelName }}>
}
@@ -0,0 +1,14 @@
/*
|--------------------------------------------------------------------------
| Web Guard
|--------------------------------------------------------------------------
|
| The web guard uses sessions for maintaining user login state. It uses
| the `user` provider for fetching user details.
|
*/
web: {
implementation: SessionGuardContract<'user', 'web'>
config: SessionGuardConfig<'user'>
client: SessionClientContract<'user'>
}
+76
View File
@@ -0,0 +1,76 @@
import { AuthenticationException } from '@adonisjs/auth/build/standalone'
import type { GuardsList } from '@ioc:Adonis/Addons/Auth'
import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
/**
* Auth middleware is meant to restrict un-authenticated access to a given route
* or a group of routes.
*
* You must register this middleware inside `start/kernel.ts` file under the list
* of named middleware.
*/
export default class AuthMiddleware {
/**
* The URL to redirect to when request is Unauthorized
*/
protected redirectTo = '/login'
/**
* Authenticates the current HTTP request against a custom set of defined
* guards.
*
* The authentication loop stops as soon as the user is authenticated using any
* of the mentioned guards and that guard will be used by the rest of the code
* during the current request.
*/
protected async authenticate(auth: HttpContextContract['auth'], guards: (keyof GuardsList)[]) {
/**
* Hold reference to the guard last attempted within the for loop. We pass
* the reference of the guard to the "AuthenticationException", so that
* it can decide the correct response behavior based upon the guard
* driver
*/
let guardLastAttempted: string | undefined
for (let guard of guards) {
guardLastAttempted = guard
if (await auth.use(guard).check()) {
/**
* Instruct auth to use the given guard as the default guard for
* the rest of the request, since the user authenticated
* succeeded here
*/
auth.defaultGuard = guard
return true
}
}
/**
* Unable to authenticate using any guard
*/
throw new AuthenticationException(
'Unauthorized access',
'E_UNAUTHORIZED_ACCESS',
guardLastAttempted,
this.redirectTo,
)
}
/**
* Handle request
*/
public async handle (
{ auth }: HttpContextContract,
next: () => Promise<void>,
customGuards: (keyof GuardsList)[]
) {
/**
* Uses the user defined guards or the default guard mentioned in
* the config file
*/
const guards = customGuards.length ? customGuards : [auth.name]
await this.authenticate(auth, guards)
await next()
}
}
+21
View File
@@ -0,0 +1,21 @@
import type { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
/**
* Silent auth middleware can be used as a global middleware to silent check
* if the user is logged-in or not.
*
* The request continues as usual, even when the user is not logged-in.
*/
export default class SilentAuthMiddleware {
/**
* Handle request
*/
public async handle({ auth }: HttpContextContract, next: () => Promise<void>) {
/**
* Check if user is logged-in or not. If yes, then `ctx.auth.user` will be
* set to the instance of the currently logged in user.
*/
await auth.check()
await next()
}
}
+25
View File
@@ -0,0 +1,25 @@
import BaseSchema from '@ioc:Adonis/Lucid/Schema'
export default class extends BaseSchema {
protected tableName = '{{ tokensTableName }}'
public async up() {
this.schema.createTable(this.tableName, (table) => {
table.increments('id').primary()
table.integer('user_id').unsigned().references('id').inTable('{{ usersTableName }}').onDelete('CASCADE')
table.string('name').notNullable()
table.string('type').notNullable()
table.string('token', 64).notNullable().unique()
/**
* Uses timestampz for PostgreSQL and DATETIME2 for MSSQL
*/
table.timestamp('expires_at', { useTz: true }).nullable()
table.timestamp('created_at', { useTz: true }).notNullable()
})
}
public async down() {
this.schema.dropTable(this.tableName)
}
}
+24
View File
@@ -0,0 +1,24 @@
import BaseSchema from '@ioc:Adonis/Lucid/Schema'
export default class extends BaseSchema {
protected tableName = '{{ usersTableName }}'
public async up() {
this.schema.createTable(this.tableName, (table) => {
table.increments('id').primary()
table.string('email', 255).notNullable().unique()
table.string('password', 180).notNullable()
table.string('remember_me_token').nullable()
/**
* Uses timestampz for PostgreSQL and DATETIME2 for MSSQL
*/
table.timestamp('created_at', { useTz: true }).notNullable()
table.timestamp('updated_at', { useTz: true }).notNullable()
})
}
public async down() {
this.schema.dropTable(this.tableName)
}
}
+30
View File
@@ -0,0 +1,30 @@
import { DateTime } from 'luxon'
import Hash from '@ioc:Adonis/Core/Hash'
import { column, beforeSave, BaseModel } from '@ioc:Adonis/Lucid/Orm'
export default class {{ modelName }} extends BaseModel {
@column({ isPrimary: true })
public id: number
@column()
public email: string
@column({ serializeAs: null })
public password: string
@column()
public rememberMeToken: string | null
@column.dateTime({ autoCreate: true })
public createdAt: DateTime
@column.dateTime({ autoCreate: true, autoUpdate: true })
public updatedAt: DateTime
@beforeSave()
public static async hashPassword ({{ modelReference }}: {{ modelName }}) {
if ({{ modelReference }}.$dirty.password) {
{{ modelReference }}.password = await Hash.make({{ modelReference }}.password)
}
}
}
+9
View File
@@ -0,0 +1,9 @@
# The MIT License
Copyright 2021 Harminder Virk, contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+147
View File
@@ -0,0 +1,147 @@
<div align="center"><img src="https://res.cloudinary.com/adonisjs/image/upload/q_100/v1557762307/poppinss_iftxlt.jpg" width="600px"></div>
# Hooks
> A no brainer module to execute lifecycle hooks in sequence.
[![gh-workflow-image]][gh-workflow-url] [![typescript-image]][typescript-url] [![npm-image]][npm-url] [![license-image]][license-url] [![synk-image]][synk-url]
I find myself re-writing the code for hooks in multiple packages, so decided to extract it to it's own module, that can be re-used by other modules of AdonisJS.
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
## Table of contents
- [How it works?](#how-it-works)
- [Installation](#installation)
- [Usage](#usage)
- [API](#api)
- [add(lifecycle: 'before' | 'after', action: string, handler: Function | string)](#addlifecycle-before--after-action-string-handler-function--string)
- [exec(lifecycle: 'before' | 'after', action: string, ...data: any[])](#execlifecycle-before--after-action-string-data-any)
- [remove (lifecycle: 'before' | 'after', action: string, handler: HooksHandler | string)](#remove-lifecycle-before--after-action-string-handler-hookshandler--string)
- [clear(lifecycle: 'before' | 'after', action?: string)](#clearlifecycle-before--after-action-string)
- [merge (hooks: Hooks): void](#merge-hooks-hooks-void)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
## How it works?
The hooks class exposes the API to `register`, `remove` and `exec` lifecycle hooks for any number of actions or events. The class API is meant to be used internally and not by the user facing code and this gives you the chance to improve the hooks DX.
For example: The Lucid models uses this class internally and expose `before` and `after` methods on the model itself. Doing this, Lucid can control the autocomplete, type checking for the `before` and `after` methods itself, without relying on this package to expose the generics API.
> Also generics increases the number of types Typescript has to generate and it's better to avoid them whenever possible.
## Installation
Install the package from npm registry as follows:
```sh
npm i @poppinss/hooks
# yarn
yarn add @poppinss/hooks
```
## Usage
Use it as follows
```ts
import { Hooks } from '@poppinss/hooks'
const hooks = new Hooks()
hooks.add('before', 'save', function () {})
// Later invoke before save hooks
await hooks.exec('before', 'save', { id: 1 })
```
If you want the end user to define IoC container bindings as the hook handler, then you need to pass the `IoC` container resolver to the Hooks constructor. Following is the snippet from Lucid models.
```ts
import { Ioc } from '@adonisjs/fold'
const ioc = new Ioc()
const resolver = ioc.getResolver(undefined, 'modelHooks', 'App/Models/Hooks')
const hooks = new Hooks(resolver)
```
The resolver allows the end user to pass the hook reference as string and hooks must live inside `App/Models/Hooks` folder.
```ts
hooks.add('before', 'save', 'User.encryptPassword')
```
## API
#### add(lifecycle: 'before' | 'after', action: string, handler: Function | string)
Add a new hook handler.
```ts
hooks.add('before', 'save', (data) => {
console.log(data)
})
```
#### exec(lifecycle: 'before' | 'after', action: string, ...data: any[])
Execute a given hook for a selected lifecycle.
```ts
hooks.exec('before', 'save', { username: 'virk' })
```
#### remove (lifecycle: 'before' | 'after', action: string, handler: HooksHandler | string)
Remove an earlier registered hook. If you are using the IoC container bindings, then passing the binding string is enough, otherwise you need to store the reference of the function.
```ts
function onSave() {}
hooks.add('before', 'save', onSave)
// Later remove it
hooks.remove('before', 'save', onSave)
```
#### clear(lifecycle: 'before' | 'after', action?: string)
Clear all hooks for a given lifecycle and optionally an action.
```ts
hooks.clear('before')
// Clear just for the save action
hooks.clear('before', 'save')
```
#### merge (hooks: Hooks): void
Merge hooks from an existing hooks instance. Useful during class inheritance.
```ts
const hooks = new Hooks()
hooks.add('before', 'save', function () {})
const hooks1 = new Hooks()
hooks1.merge(hooks)
await hooks1.exec('before', 'save', [])
```
[gh-workflow-image]: https://img.shields.io/github/workflow/status/poppinss/hooks/test?style=for-the-badge
[gh-workflow-url]: https://github.com/poppinss/hooks/actions/workflows/test.yml "Github action"
[typescript-image]: https://img.shields.io/badge/Typescript-294E80.svg?style=for-the-badge&logo=typescript
[typescript-url]: "typescript"
[npm-image]: https://img.shields.io/npm/v/@poppinss/hooks.svg?style=for-the-badge&logo=npm
[npm-url]: https://npmjs.org/package/@poppinss/hooks 'npm'
[license-image]: https://img.shields.io/npm/l/@poppinss/hooks?color=blueviolet&style=for-the-badge
[license-url]: LICENSE.md 'license'
[synk-image]: https://img.shields.io/snyk/vulnerabilities/github/poppinss/hooks?label=Synk%20Vulnerabilities&style=for-the-badge
[synk-url]: https://snyk.io/test/github/poppinss/hooks?targetFile=package.json 'synk'
@@ -0,0 +1 @@
export { Hooks } from './src/Hooks';
@@ -0,0 +1,13 @@
"use strict";
/*
* @poppinss/hooks
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Hooks = void 0;
var Hooks_1 = require("./src/Hooks");
Object.defineProperty(exports, "Hooks", { enumerable: true, get: function () { return Hooks_1.Hooks; } });
@@ -0,0 +1,59 @@
/// <reference types="@adonisjs/application/build/adonis-typings/application" />
declare type HooksHandler = (...args: any[]) => void | Promise<void>;
/**
* Exposes the API to register before/after lifecycle hooks for a given action
* with option to resolve handlers from the IoC container.
*
* The hooks class doesn't provide autocomplete for actions and the arguments
* the handler will receive, since we expect this class to be used internally
* for user facing objects.
*/
export declare class Hooks {
private resolver?;
private hooks;
constructor(resolver?: import("@ioc:Adonis/Core/Application").IocResolverContract<import("@ioc:Adonis/Core/Application").ContainerBindings> | undefined);
/**
* Raise exceptins when resolver is not defined
*/
private ensureResolver;
/**
* Resolves the hook handler using the resolver when it is defined as string
* or returns the function reference back
*/
private resolveHandler;
/**
* Returns handlers set for a given action or undefined
*/
private getActionHandlers;
/**
* Adds the resolved handler to the actions set
*/
private addResolvedHandler;
/**
* Returns a boolean whether a handler has been already registered or not
*/
has(lifecycle: 'before' | 'after', action: string, handler: HooksHandler | string): boolean;
/**
* Register hook handler for a given event and lifecycle
*/
add(lifecycle: 'before' | 'after', action: string, handler: HooksHandler | string): this;
/**
* Remove a pre-registered handler
*/
remove(lifecycle: 'before' | 'after', action: string, handler: HooksHandler | string): void;
/**
* Remove all handlers for a given action or lifecycle. If action is not
* defined, then all actions for that given lifecycle are removed
*/
clear(lifecycle: 'before' | 'after', action?: string): void;
/**
* Merges hooks of a given hook instance. To merge from more than
* one instance, you can call the merge method for multiple times
*/
merge(hooks: Hooks): void;
/**
* Executes the hook handler for a given action and lifecycle
*/
exec(lifecycle: 'before' | 'after', action: string, ...data: any[]): Promise<void>;
}
export {};
@@ -0,0 +1,137 @@
"use strict";
/*
* @poppinss/hooks
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Hooks = void 0;
/**
* Exposes the API to register before/after lifecycle hooks for a given action
* with option to resolve handlers from the IoC container.
*
* The hooks class doesn't provide autocomplete for actions and the arguments
* the handler will receive, since we expect this class to be used internally
* for user facing objects.
*/
class Hooks {
constructor(resolver) {
this.resolver = resolver;
this.hooks = {
before: new Map(),
after: new Map(),
};
}
/**
* Raise exceptins when resolver is not defined
*/
ensureResolver() {
if (!this.resolver) {
throw new Error('IoC container resolver is required to register string based hooks handlers');
}
}
/**
* Resolves the hook handler using the resolver when it is defined as string
* or returns the function reference back
*/
resolveHandler(handler) {
if (typeof handler === 'string') {
this.ensureResolver();
return this.resolver.resolve(handler);
}
return handler;
}
/**
* Returns handlers set for a given action or undefined
*/
getActionHandlers(lifecycle, action) {
return this.hooks[lifecycle].get(action);
}
/**
* Adds the resolved handler to the actions set
*/
addResolvedHandler(lifecycle, action, handler) {
const handlers = this.getActionHandlers(lifecycle, action);
if (handlers) {
handlers.add(handler);
}
else {
this.hooks[lifecycle].set(action, new Set([handler]));
}
}
/**
* Returns a boolean whether a handler has been already registered or not
*/
has(lifecycle, action, handler) {
const handlers = this.getActionHandlers(lifecycle, action);
if (!handlers) {
return false;
}
return handlers.has(this.resolveHandler(handler));
}
/**
* Register hook handler for a given event and lifecycle
*/
add(lifecycle, action, handler) {
this.addResolvedHandler(lifecycle, action, this.resolveHandler(handler));
return this;
}
/**
* Remove a pre-registered handler
*/
remove(lifecycle, action, handler) {
const handlers = this.getActionHandlers(lifecycle, action);
if (!handlers) {
return;
}
handlers.delete(this.resolveHandler(handler));
}
/**
* Remove all handlers for a given action or lifecycle. If action is not
* defined, then all actions for that given lifecycle are removed
*/
clear(lifecycle, action) {
if (!action) {
this.hooks[lifecycle].clear();
return;
}
this.hooks[lifecycle].delete(action);
}
/**
* Merges hooks of a given hook instance. To merge from more than
* one instance, you can call the merge method for multiple times
*/
merge(hooks) {
hooks.hooks.before.forEach((actionHooks, action) => {
actionHooks.forEach((handler) => {
this.addResolvedHandler('before', action, handler);
});
});
hooks.hooks.after.forEach((actionHooks, action) => {
actionHooks.forEach((handler) => {
this.addResolvedHandler('after', action, handler);
});
});
}
/**
* Executes the hook handler for a given action and lifecycle
*/
async exec(lifecycle, action, ...data) {
const handlers = this.getActionHandlers(lifecycle, action);
if (!handlers) {
return;
}
for (let handler of handlers) {
if (typeof handler === 'function') {
await handler(...data);
}
else {
await this.resolver.call(handler, undefined, data);
}
}
}
}
exports.Hooks = Hooks;
+134
View File
@@ -0,0 +1,134 @@
{
"name": "@poppinss/hooks",
"version": "5.0.3",
"description": "A no brainer hooks module for execute before/after lifecycle hooks",
"main": "build/index.js",
"files": [
"build/src",
"build/index.d.ts",
"build/index.js"
],
"scripts": {
"mrm": "mrm --preset=@adonisjs/mrm-preset",
"pretest": "npm run lint",
"test": "node .bin/test.js",
"clean": "del build",
"compile": "npm run lint && npm run clean && tsc",
"build": "npm run compile",
"commit": "git-cz",
"release": "np --message=\"chore(release): %s\"",
"version": "npm run build",
"format": "prettier --write .",
"prepublishOnly": "npm run build",
"lint": "eslint . --ext=.ts",
"sync-labels": "github-label-sync --labels ./node_modules/@adonisjs/mrm-preset/gh-labels.json poppinss/hooks"
},
"peerDependencies": {
"@adonisjs/application": ">=4.0.0"
},
"peerDependenciesMeta": {
"@adonisjs/application": {
"optional": true
}
},
"devDependencies": {
"@adonisjs/application": "^5.2.0",
"@adonisjs/mrm-preset": "^5.0.3",
"@adonisjs/require-ts": "^2.0.11",
"@types/node": "^17.0.23",
"commitizen": "^4.2.4",
"cz-conventional-changelog": "^3.3.0",
"del-cli": "^4.0.1",
"doctoc": "^2.0.1",
"eslint": "^8.12.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-adonis": "^2.1.0",
"eslint-plugin-prettier": "^4.0.0",
"github-label-sync": "^2.2.0",
"husky": "^7.0.1",
"japa": "^4.0.0",
"mrm": "^4.0.0",
"np": "^7.6.1",
"prettier": "^2.6.2",
"typescript": "^4.6.3"
},
"repository": {
"type": "git",
"url": "git+https://github.com/poppinss/hooks.git"
},
"keywords": [
"hooks",
"poppinss"
],
"author": "virk,poppinss",
"license": "MIT",
"bugs": {
"url": "https://github.com/poppinss/hooks/issues"
},
"homepage": "https://github.com/poppinss/hooks#readme",
"nyc": {
"exclude": [
"test"
],
"extension": [
".ts"
]
},
"husky": {
"hooks": {
"commit-msg": "node ./node_modules/@adonisjs/mrm-preset/validateCommit/conventional/validate.js"
}
},
"config": {
"commitizen": {
"path": "cz-conventional-changelog"
}
},
"np": {
"contents": ".",
"anyBranch": false
},
"mrmConfig": {
"core": false,
"license": "MIT",
"services": [
"github-actions"
],
"minNodeVersion": "16.13.1",
"probotApps": [
"stale",
"lock"
],
"runGhActionsOnWindows": false
},
"eslintConfig": {
"extends": [
"plugin:adonis/typescriptPackage",
"prettier"
],
"plugins": [
"prettier"
],
"rules": {
"prettier/prettier": [
"error",
{
"endOfLine": "auto"
}
]
}
},
"eslintIgnore": [
"build"
],
"prettier": {
"trailingComma": "es5",
"semi": false,
"singleQuote": true,
"useTabs": false,
"quoteProps": "consistent",
"bracketSpacing": true,
"arrowParens": "always",
"printWidth": 100
}
}
+140
View File
@@ -0,0 +1,140 @@
{
"name": "@adonisjs/auth",
"version": "8.2.3",
"description": "Official authentication provider for Adonis framework",
"types": "build/adonis-typings/index.d.ts",
"main": "build/providers/AuthProvider.js",
"files": [
"build/adonis-typings",
"build/providers",
"build/templates",
"build/src",
"build/instructions.js",
"build/standalone.js",
"build/standalone.d.ts"
],
"scripts": {
"mrm": "mrm --preset=@adonisjs/mrm-preset",
"pretest": "npm run lint",
"test": "node -r @adonisjs/require-ts/build/register ./bin/test.ts",
"clean": "del-cli build",
"copyfiles": "copyfiles \"templates/**/*.txt\" build",
"compile": "npm run lint && npm run clean && tsc",
"build": "npm run compile && npm run copyfiles",
"commit": "git-cz",
"release": "np",
"version": "npm run build",
"lint": "eslint . --ext=.ts",
"prepublishOnly": "npm run build",
"sync-labels": "github-label-sync --labels ./node_modules/@adonisjs/mrm-preset/gh-labels.json adonisjs/auth",
"format": "prettier --write ."
},
"keywords": [
"adonis",
"adonis-framework",
"adonis-auth",
"authentication"
],
"author": "adonisjs,virk",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/adonisjs/auth.git"
},
"homepage": "https://github.com/adonisjs/auth#readme",
"bugs": {
"url": "https://github.com/adonisjs/auth/issues"
},
"devDependencies": {
"@adonisjs/core": "^5.8.8",
"@adonisjs/i18n": "^1.5.6",
"@adonisjs/lucid": "^18.2.0",
"@adonisjs/mrm-preset": "^5.0.3",
"@adonisjs/redis": "^7.3.1",
"@adonisjs/repl": "^3.1.11",
"@adonisjs/require-ts": "^2.0.13",
"@adonisjs/session": "^6.4.0",
"@adonisjs/sink": "^5.4.1",
"@japa/assert": "^1.3.6",
"@japa/preset-adonis": "^1.2.0",
"@japa/run-failed-tests": "^1.1.0",
"@japa/runner": "^2.2.2",
"@japa/spec-reporter": "^1.3.2",
"@poppinss/dev-utils": "^2.0.3",
"@types/node": "^18.11.2",
"@types/supertest": "^2.0.12",
"copyfiles": "^2.4.1",
"del-cli": "^5.0.0",
"eslint": "^8.25.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-adonis": "^2.1.1",
"eslint-plugin-prettier": "^4.2.1",
"github-label-sync": "^2.2.0",
"husky": "^8.0.1",
"mrm": "^4.1.13",
"np": "^7.6.2",
"phc-bcrypt": "^1.0.7",
"pino-pretty": "^9.1.1",
"prettier": "^2.7.1",
"reflect-metadata": "^0.1.13",
"set-cookie-parser": "^2.5.1",
"sqlite3": "^5.1.2",
"supertest": "^6.3.0",
"ts-essentials": "^9.3.0",
"typescript": "^4.8.4"
},
"nyc": {
"exclude": [
"test"
],
"extension": [
".ts"
]
},
"config": {
"commitizen": {
"path": "cz-conventional-changelog"
}
},
"np": {
"contents": ".",
"anyBranch": false
},
"dependencies": {
"@poppinss/hooks": "^5.0.3",
"@poppinss/utils": "^5.0.0",
"luxon": "^3.0.4"
},
"peerDependencies": {
"@adonisjs/core": "^5.7.1",
"@adonisjs/i18n": "^1.5.0",
"@adonisjs/lucid": "^18.0.0",
"@adonisjs/redis": "^7.2.0",
"@adonisjs/session": "^6.2.0"
},
"peerDependenciesMeta": {
"@adonisjs/i18n": {
"optional": true
},
"@adonisjs/lucid": {
"optional": true
},
"@adonisjs/session": {
"optional": true
},
"@adonisjs/redis": {
"optional": true
}
},
"publishConfig": {
"access": "public",
"tag": "latest"
},
"adonisjs": {
"instructions": "./build/instructions.js",
"types": "@adonisjs/auth",
"providers": [
"@adonisjs/auth"
]
}
}