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
+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)
}
}
}