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
+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;