Skip to content

Commit

Permalink
authentication: bind current user in the context
Browse files Browse the repository at this point in the history
plus general cleanup.
  • Loading branch information
bajtos committed Aug 8, 2017
1 parent c507e37 commit 7b7dda5
Show file tree
Hide file tree
Showing 6 changed files with 35 additions and 24 deletions.
1 change: 1 addition & 0 deletions packages/authentication/src/keys.ts
Expand Up @@ -11,5 +11,6 @@ export namespace BindingKeys {
export const STRATEGY = 'authentication.strategy';
export const AUTH_ACTION = 'authentication.actions.authenticate';
export const METADATA = 'authentication.operationMetadata';
export const CURRENT_USER = 'authentication.currentUser';
}
}
33 changes: 17 additions & 16 deletions packages/authentication/src/providers/authenticate.ts
Expand Up @@ -5,7 +5,7 @@

import * as http from 'http';
import {HttpErrors, inject, ParsedRequest} from '@loopback/core';
import {Provider} from '@loopback/context';
import {Provider, Getter, Setter} from '@loopback/context';
import {Strategy} from 'passport';
import {StrategyAdapter} from '../strategy-adapter';
import {BindingKeys} from '../keys';
Expand Down Expand Up @@ -44,41 +44,42 @@ export class AuthenticationProvider implements Provider<AuthenticateFn> {
// is executed.
@inject.getter(BindingKeys.Authentication.STRATEGY)
readonly getStrategy: () => Promise<Strategy>,
@inject.setter(BindingKeys.Authentication.CURRENT_USER)
readonly setCurrentUser: (value: UserProfile) => void,
) {}

/**
* @returns authenticateFn
*/
value(): AuthenticateFn {
return async (request: ParsedRequest) => {
const strategy = await this.getStrategy();
return await getAuthenticatedUser(strategy, request);
return await authenticateRequest(
this.getStrategy,
request,
this.setCurrentUser,
);
};
}
}

/**
* @description a function which accepts (passport-strategy, request)
* and returns a user
* The implementation of authenticate() sequence action.
* @param strategy Passport strategy
* @param request Parsed Request
*
* @example
* ```ts
* const strategy = new BasicStrategy(async (username, password) => {
* return await findUser(username, password);
* };
* getAuthenticatedUser(strategy, ParsedRequest);
* ```
* @param setCurrentUser The setter function to update the current user
* in the per-request Context
*/
export async function getAuthenticatedUser(
strategy: Strategy,
async function authenticateRequest(
getStrategy: Getter<Strategy>,
request: ParsedRequest,
setCurrentUser: Setter<UserProfile>,
): Promise<UserProfile> {
const strategy = await getStrategy();
if (!strategy.authenticate) {
return Promise.reject(new Error('invalid strategy parameter'));
}
const strategyAdapter = new StrategyAdapter(strategy);
const user: UserProfile = await strategyAdapter.authenticate(request);
const user = await strategyAdapter.authenticate(request);
setCurrentUser(user);
return user;
}
11 changes: 5 additions & 6 deletions packages/authentication/test/acceptance/basic-auth.ts
Expand Up @@ -71,7 +71,7 @@ describe('Basic Authentication', () => {
users = new UserRepository({
joe : {profile: {id: 'joe'}, password: '12345'},
Simpson: {profile: {id: 'sim123'}, password: 'alpha'},
Flintstone: {profile: {id: 'Flint'}, password: 'beta'},
Flinstone: {profile: {id: 'Flint'}, password: 'beta'},
George: {profile: {id: 'Curious'}, password: 'gamma'},
});
}
Expand All @@ -96,7 +96,10 @@ describe('Basic Authentication', () => {

@api(apispec)
class MyController {
constructor(@inject('authentication.user') private user: UserProfile) {}
constructor(
@inject(BindingKeys.Authentication.CURRENT_USER)
private user: UserProfile,
) {}

@authenticate('BasicStrategy')
async whoAmI() : Promise<string> {
Expand Down Expand Up @@ -125,10 +128,6 @@ describe('Basic Authentication', () => {
// Authenticate
const user: UserProfile = await this.authenticateRequest(req);

// User is expected to be returned or an exception should be thrown
if (user) this.bindElement('authentication.user').to(user);
else throw new HttpErrors.InternalServerError('auth error');

// Authentication successful, proceed to invoke controller
const args = await parseOperationArgs(req, route);
const result = await this.invoke(route, args);
Expand Down
Expand Up @@ -8,7 +8,6 @@ import {Context, Provider, instantiateClass} from '@loopback/context';
import {ParsedRequest} from '@loopback/core';
import {
AuthenticationProvider,
getAuthenticatedUser,
AuthenticateFn,
UserProfile,
BindingKeys,
Expand All @@ -30,6 +29,7 @@ describe('AuthenticationProvider', () => {
describe('value()', () => {
let provider: AuthenticationProvider;
let strategy: MockStrategy;
let currentUser: UserProfile | undefined;

const mockUser: UserProfile = {name: 'user-name', id: 'mock-id'};

Expand All @@ -45,6 +45,13 @@ describe('AuthenticationProvider', () => {
expect(user).to.be.equal(mockUser);
});

it('updates current user', async () => {
const authenticate = await Promise.resolve(provider.value());
const request = <ParsedRequest> {};
await authenticate(request);
expect(currentUser).to.equal(mockUser);
});

describe('context.get(provider_key)', () => {
it('returns a function which authenticates a request and returns a user',
async () => {
Expand Down Expand Up @@ -106,7 +113,10 @@ describe('AuthenticationProvider', () => {
function givenAuthenticationProvider() {
strategy = new MockStrategy();
strategy.setMockUser(mockUser);
provider = new AuthenticationProvider(() => Promise.resolve(strategy));
provider = new AuthenticationProvider(
() => Promise.resolve(strategy),
u => currentUser = u);
currentUser = undefined;
}
});
});

0 comments on commit 7b7dda5

Please sign in to comment.