From 7edb7eeb28b6f57f43eb39249529fbf60b6a4bd2 Mon Sep 17 00:00:00 2001 From: dremond Date: Fri, 3 May 2019 14:40:54 -0400 Subject: [PATCH] chore: use username instead of email for basic use username instead of email for basic authentication --- .../basic-auth-extension.acceptance.ts | 43 ++---- .../jwt-auth-extension.acceptance.ts | 136 +++++------------- .../src/__tests__/fixtures/helper.ts | 38 +++-- .../services/basic-auth-user-service.ts | 31 +--- .../fixtures/services/jwt-service.ts | 22 +-- .../fixtures/strategies/basic-strategy.ts | 6 +- .../fixtures/users/user.repository.ts | 4 +- .../src/__tests__/fixtures/users/user.ts | 2 +- 8 files changed, 86 insertions(+), 196 deletions(-) diff --git a/packages/authentication/src/__tests__/acceptance/basic-auth-extension.acceptance.ts b/packages/authentication/src/__tests__/acceptance/basic-auth-extension.acceptance.ts index 01a69c5ced64..e1eec56b9ea8 100644 --- a/packages/authentication/src/__tests__/acceptance/basic-auth-extension.acceptance.ts +++ b/packages/authentication/src/__tests__/acceptance/basic-auth-extension.acceptance.ts @@ -25,12 +25,14 @@ import {BasicAuthenticationStrategyBindings, USER_REPO} from '../fixtures/keys'; import {MyAuthenticationSequence} from '../fixtures/sequences/authentication.sequence'; import {BasicAuthenticationUserService} from '../fixtures/services/basic-auth-user-service'; import {BasicAuthenticationStrategy} from '../fixtures/strategies/basic-strategy'; +import {User} from '../fixtures/users/user'; import {UserRepository} from '../fixtures/users/user.repository'; describe('Basic Authentication', () => { let app: Application; let server: RestServer; let users: UserRepository; + let joeUser: User; beforeEach(givenAServer); beforeEach(givenControllerInApp); beforeEach(givenAuthenticatedSequence); @@ -40,14 +42,8 @@ describe('Basic Authentication', () => { const client = whenIMakeRequestTo(server); await client .get('/whoAmI') - .set( - 'Authorization', - createBasicAuthorizationHeaderValue( - users.list['joe@example.com'].email, - users.list['joe@example.com'].password, - ), - ) - .expect(users.list['joe@example.com'].email); + .set('Authorization', createBasicAuthorizationHeaderValue(joeUser)) + .expect(joeUser.id); }); it('returns error for missing Authorization header', async () => { @@ -68,11 +64,7 @@ describe('Basic Authentication', () => { .get('/whoAmI') .set( 'Authorization', - createBasicAuthorizationHeaderValue( - users.list['joe@example.com'].email, - users.list['joe@example.com'].password, - {prefix: 'NotB@sic '}, - ), + createBasicAuthorizationHeaderValue(joeUser, {prefix: 'NotB@sic '}), ) .expect({ error: { @@ -89,10 +81,7 @@ describe('Basic Authentication', () => { .get('/whoAmI') .set( 'Authorization', - createBasicAuthorizationHeaderValue( - users.list['joe@example.com'].email, - users.list['joe@example.com'].password, - ) + ' someOtherValue', + createBasicAuthorizationHeaderValue(joeUser) + ' someOtherValue', ) .expect({ error: { @@ -109,11 +98,7 @@ describe('Basic Authentication', () => { .get('/whoAmI') .set( 'Authorization', - createBasicAuthorizationHeaderValue( - users.list['joe@example.com'].email, - users.list['joe@example.com'].password, - {separator: '|'}, - ), + createBasicAuthorizationHeaderValue(joeUser, {separator: '|'}), ) .expect({ error: { @@ -130,11 +115,9 @@ describe('Basic Authentication', () => { .get('/whoAmI') .set( 'Authorization', - createBasicAuthorizationHeaderValue( - users.list['joe@example.com'].email, - users.list['joe@example.com'].password, - {extraSegment: 'extraPart'}, - ), + createBasicAuthorizationHeaderValue(joeUser, { + extraSegment: 'extraPart', + }), ) .expect({ error: { @@ -238,8 +221,8 @@ describe('Basic Authentication', () => { @inject(AuthenticationBindings.CURRENT_USER) userProfile: UserProfile, ): Promise { if (!userProfile) return 'userProfile is undefined'; - if (!userProfile.email) return 'userProfile email is undefined'; - return userProfile.email; + if (!userProfile.id) return 'userProfile id is undefined'; + return userProfile.id; } } app.controller(MyController); @@ -258,7 +241,7 @@ describe('Basic Authentication', () => { .toClass(BasicAuthenticationUserService); users = getUserRepository(); - + joeUser = users.list['joe888']; server.bind(USER_REPO).to(users); } diff --git a/packages/authentication/src/__tests__/acceptance/jwt-auth-extension.acceptance.ts b/packages/authentication/src/__tests__/acceptance/jwt-auth-extension.acceptance.ts index 83dfa582fc2d..69d6341cf87b 100644 --- a/packages/authentication/src/__tests__/acceptance/jwt-auth-extension.acceptance.ts +++ b/packages/authentication/src/__tests__/acceptance/jwt-auth-extension.acceptance.ts @@ -17,6 +17,7 @@ import { } from '../..'; import { createBearerAuthorizationHeaderValue, + createUserProfile, getApp, getUserRepository, } from '../fixtures/helper'; @@ -24,12 +25,14 @@ import {JWTAuthenticationStrategyBindings, USER_REPO} from '../fixtures/keys'; import {MyAuthenticationSequence} from '../fixtures/sequences/authentication.sequence'; import {JWTService} from '../fixtures/services/jwt-service'; import {JWTAuthenticationStrategy} from '../fixtures/strategies/jwt-strategy'; +import {User} from '../fixtures/users/user'; import {UserRepository} from '../fixtures/users/user.repository'; describe('JWT Authentication', () => { let app: Application; let server: RestServer; let testUsers: UserRepository; + let joeUser: User; let token: string; beforeEach(givenAServer); @@ -52,15 +55,9 @@ describe('JWT Authentication', () => { // // Now with a valid userProfile, let's create a JSON web token - const joeUser = this.users.list['joe@example.com']; - - const joeUserProfile = { - id: joeUser.id, - email: joeUser.email, - name: `${joeUser.firstname} ${joeUser.surname}`, - }; - - return await this.tokenService.generateToken(joeUserProfile); + return await this.tokenService.generateToken( + createUserProfile(joeUser), + ); } @get('/whoAmI') @@ -69,8 +66,8 @@ describe('JWT Authentication', () => { @inject(AuthenticationBindings.CURRENT_USER) userProfile: UserProfile, ) { if (!userProfile) return 'userProfile is undefined'; - if (!userProfile.email) return 'userProfile email is undefined'; - return userProfile.email; + if (!userProfile.id) return 'userProfile id is undefined'; + return userProfile.id; } } @@ -83,12 +80,12 @@ describe('JWT Authentication', () => { expect(token).to.be.not.null(); expect(token).to.be.String(); - const email = (await whenIMakeRequestTo(server) + const id = (await whenIMakeRequestTo(server) .get('/whoAmI') .set('Authorization', createBearerAuthorizationHeaderValue(token)) .expect(200)).text; - expect(email).to.equal(testUsers.list['joe@example.com'].email); + expect(id).to.equal(joeUser.id); }); it(`returns error for missing Authorization header`, async () => { @@ -107,15 +104,9 @@ describe('JWT Authentication', () => { // // Now with a valid userProfile, let's create a JSON web token - const joeUser = this.users.list['joe@example.com']; - - const joeUserProfile = { - id: joeUser.id, - email: joeUser.email, - name: `${joeUser.firstname} ${joeUser.surname}`, - }; - - return await this.tokenService.generateToken(joeUserProfile); + return await this.tokenService.generateToken( + createUserProfile(joeUser), + ); } @get('/whoAmI') @@ -124,8 +115,8 @@ describe('JWT Authentication', () => { @inject(AuthenticationBindings.CURRENT_USER) userProfile: UserProfile, ) { if (!userProfile) return 'userProfile is undefined'; - if (!userProfile.email) return 'userProfile email is undefined'; - return userProfile.email; + if (!userProfile.id) return 'userProfile id is undefined'; + return userProfile.id; } } @@ -165,15 +156,9 @@ describe('JWT Authentication', () => { // // Now with a valid userProfile, let's create a JSON web token - const joeUser = this.users.list['joe@example.com']; - - const joeUserProfile = { - id: joeUser.id, - email: joeUser.email, - name: `${joeUser.firstname} ${joeUser.surname}`, - }; - - return await this.tokenService.generateToken(joeUserProfile); + return await this.tokenService.generateToken( + createUserProfile(joeUser), + ); } @get('/whoAmI') @@ -182,8 +167,8 @@ describe('JWT Authentication', () => { @inject(AuthenticationBindings.CURRENT_USER) userProfile: UserProfile, ) { if (!userProfile) return 'userProfile is undefined'; - if (!userProfile.email) return 'userProfile email is undefined'; - return userProfile.email; + if (!userProfile.id) return 'userProfile id is undefined'; + return userProfile.id; } } @@ -226,16 +211,9 @@ describe('JWT Authentication', () => { // ...Other code for verifying a valid user (e.g. basic or local strategy)... // - // Now with a valid userProfile, let's create a JSON web token - const joeUser = this.users.list['joe@example.com']; - - const joeUserProfile = { - id: joeUser.id, - email: joeUser.email, - name: `${joeUser.firstname} ${joeUser.surname}`, - }; - - return await this.tokenService.generateToken(joeUserProfile); + return await this.tokenService.generateToken( + createUserProfile(joeUser), + ); } @get('/whoAmI') @@ -244,8 +222,8 @@ describe('JWT Authentication', () => { @inject(AuthenticationBindings.CURRENT_USER) userProfile: UserProfile, ) { if (!userProfile) return 'userProfile is undefined'; - if (!userProfile.email) return 'userProfile email is undefined'; - return userProfile.email; + if (!userProfile.id) return 'userProfile id is undefined'; + return userProfile.id; } } @@ -283,8 +261,8 @@ describe('JWT Authentication', () => { @inject(AuthenticationBindings.CURRENT_USER) userProfile: UserProfile, ) { if (!userProfile) return 'userProfile is undefined'; - if (!userProfile.email) return 'userProfile email is undefined'; - return userProfile.email; + if (!userProfile.id) return 'userProfile id is undefined'; + return userProfile.id; } } @@ -315,8 +293,8 @@ describe('JWT Authentication', () => { @inject(AuthenticationBindings.CURRENT_USER) userProfile: UserProfile, ) { if (!userProfile) return 'userProfile is undefined'; - if (!userProfile.email) return 'userProfile email is undefined'; - return userProfile.email; + if (!userProfile.id) return 'userProfile id is undefined'; + return userProfile.id; } } @@ -346,8 +324,8 @@ describe('JWT Authentication', () => { @inject(AuthenticationBindings.CURRENT_USER) userProfile: UserProfile, ) { if (!userProfile) return 'userProfile is undefined'; - if (!userProfile.email) return 'userProfile email is undefined'; - return userProfile.email; + if (!userProfile.id) return 'userProfile id is undefined'; + return userProfile.id; } } @@ -367,7 +345,7 @@ describe('JWT Authentication', () => { }); }); - it('creates a json web token and throws error for missing email', async () => { + it('creates a json web token and throws error for userProfle that is undefined', async () => { class InfoController { constructor( @inject(JWTAuthenticationStrategyBindings.TOKEN_SERVICE) @@ -378,15 +356,7 @@ describe('JWT Authentication', () => { @get('/createtoken') async createToken() { - const joeUser = this.users.list['joe@example.com']; - - const joeUserProfile = { - id: joeUser.id, - email: undefined, - name: `${joeUser.firstname} ${joeUser.surname}`, - }; - - return await this.tokenService.generateToken(joeUserProfile); + return await this.tokenService.generateToken(undefined); } } @@ -396,43 +366,7 @@ describe('JWT Authentication', () => { .get('/createtoken') .expect({ error: { - message: `Error generating token : userProfile 'email' is null`, - name: 'UnauthorizedError', - statusCode: 401, - }, - }); - }); - - it('creates a json web token and throws error for missing name', async () => { - class InfoController { - constructor( - @inject(JWTAuthenticationStrategyBindings.TOKEN_SERVICE) - public tokenService: JWTService, - @inject(USER_REPO) - public users: UserRepository, - ) {} - - @get('/createtoken') - async createToken() { - const joeUser = this.users.list['joe@example.com']; - - const joeUserProfile = { - id: joeUser.id, - email: joeUser.email, - name: undefined, - }; - - return await this.tokenService.generateToken(joeUserProfile); - } - } - - app.controller(InfoController); - - await whenIMakeRequestTo(server) - .get('/createtoken') - .expect({ - error: { - message: `Error generating token : userProfile 'name' is null`, + message: `Error generating token : userProfile is null`, name: 'UnauthorizedError', statusCode: 401, }, @@ -527,7 +461,7 @@ describe('JWT Authentication', () => { .toClass(JWTService); testUsers = getUserRepository(); - + joeUser = testUsers.list['joe888']; server.bind(USER_REPO).to(testUsers); } diff --git a/packages/authentication/src/__tests__/fixtures/helper.ts b/packages/authentication/src/__tests__/fixtures/helper.ts index b832f4cdb58f..e3ec049604dc 100644 --- a/packages/authentication/src/__tests__/fixtures/helper.ts +++ b/packages/authentication/src/__tests__/fixtures/helper.ts @@ -5,7 +5,8 @@ import {Application} from '@loopback/core'; import {RestComponent} from '@loopback/rest'; -import {AuthenticationComponent} from '../..'; +import {AuthenticationComponent, UserProfile} from '../..'; +import {User} from './users/user'; import {UserRepository} from './users/user.repository'; /** @@ -23,32 +24,32 @@ export function getApp(): Application { */ export function getUserRepository(): UserRepository { return new UserRepository({ - 'joe@example.com': { + joe888: { id: '1', firstname: 'joe', surname: 'joeman', - email: 'joe@example.com', + username: 'joe888', password: 'joepa55w0rd', }, - 'jill@example.com': { + jill888: { id: '2', firstname: 'jill', surname: 'jillman', - email: 'jill@example.com', + username: 'jill888', password: 'jillpa55w0rd', }, - 'jack@example.com': { + jack888: { id: '3', firstname: 'jack', surname: 'jackman', - email: 'jack@example.com', + username: 'jack888', password: 'jackpa55w0rd', }, - 'janice@example.com': { + janice888: { id: '4', firstname: 'janice', surname: 'janiceman', - email: 'janice@example.com', + username: 'janice888', password: 'janicepa55w0rd', }, }); @@ -66,8 +67,7 @@ export interface BasicAuthorizationHeaderValueOptions { extraSegment?: string; } export function createBasicAuthorizationHeaderValue( - email: string, - password: string, + user: User, options?: BasicAuthorizationHeaderValueOptions, ): string { options = Object.assign( @@ -86,9 +86,9 @@ export function createBasicAuthorizationHeaderValue( return ( options.prefix + - Buffer.from(`${email}${options.separator}${password}${extraPart}`).toString( - 'base64', - ) + Buffer.from( + `${user.username}${options.separator}${user.password}${extraPart}`, + ).toString('base64') ); } @@ -100,3 +100,13 @@ export function createBearerAuthorizationHeaderValue( const prefix = alternativePrefix ? alternativePrefix : 'Bearer '; return prefix + token; } + +export function createUserProfile(user: User): UserProfile { + const userProfile = {id: '', name: ''}; + + if (user && user.id) userProfile.id = user.id; + if (user && user.firstname && user.surname) + userProfile.name = `${user.firstname} ${user.surname}`; + + return userProfile; +} diff --git a/packages/authentication/src/__tests__/fixtures/services/basic-auth-user-service.ts b/packages/authentication/src/__tests__/fixtures/services/basic-auth-user-service.ts index d33d2ebef2b7..83fd7076d61f 100644 --- a/packages/authentication/src/__tests__/fixtures/services/basic-auth-user-service.ts +++ b/packages/authentication/src/__tests__/fixtures/services/basic-auth-user-service.ts @@ -7,6 +7,7 @@ import {inject} from '@loopback/context'; import {HttpErrors} from '@loopback/rest'; import {UserService} from '../../../services/user.service'; import {UserProfile} from '../../../types'; +import {createUserProfile} from '../helper'; import {USER_REPO} from '../keys'; import {BasicAuthenticationStrategyCredentials} from '../strategies/basic-strategy'; import {User} from '../users/user'; @@ -26,18 +27,18 @@ export class BasicAuthenticationUserService throw new HttpErrors.Unauthorized(`'credentials' is null`); } - if (!credentials.email) { - throw new HttpErrors.Unauthorized(`'credentials.email' is null`); + if (!credentials.username) { + throw new HttpErrors.Unauthorized(`'credentials.username' is null`); } if (!credentials.password) { throw new HttpErrors.Unauthorized(`'credentials.password' is null`); } - const foundUser = this.userRepository.find(credentials.email); + const foundUser = this.userRepository.find(credentials.username); if (!foundUser) { throw new HttpErrors['Unauthorized']( - `User with email ${credentials.email} not found.`, + `User with username ${credentials.username} not found.`, ); } @@ -54,27 +55,9 @@ export class BasicAuthenticationUserService } if (!user.id) { - throw new HttpErrors.Unauthorized(`'user.id' is null`); + throw new HttpErrors.Unauthorized(`'user id' is null`); } - if (!user.firstname) { - throw new HttpErrors.Unauthorized(`'user.firstname' is null`); - } - - if (!user.surname) { - throw new HttpErrors.Unauthorized(`'user.surname' is null`); - } - - if (!user.email) { - throw new HttpErrors.Unauthorized(`'user.email' is null`); - } - - const userProfile: UserProfile = { - id: user.id, - name: `${user.firstname} ${user.surname}`, - email: user.email, - }; - - return userProfile; + return createUserProfile(user); } } diff --git a/packages/authentication/src/__tests__/fixtures/services/jwt-service.ts b/packages/authentication/src/__tests__/fixtures/services/jwt-service.ts index 1a5099f1fa72..aa88f14f6905 100644 --- a/packages/authentication/src/__tests__/fixtures/services/jwt-service.ts +++ b/packages/authentication/src/__tests__/fixtures/services/jwt-service.ts @@ -42,35 +42,15 @@ export class JWTService implements TokenService { return userProfile; } - async generateToken(userProfile: UserProfile): Promise { + async generateToken(userProfile: UserProfile | undefined): Promise { if (!userProfile) { throw new HttpErrors.Unauthorized( 'Error generating token : userProfile is null', ); } - if (!userProfile.id) { - throw new HttpErrors.Unauthorized( - `Error generating token : userProfile 'id' is null`, - ); - } - - if (!userProfile.email) { - throw new HttpErrors.Unauthorized( - `Error generating token : userProfile 'email' is null`, - ); - } - - if (!userProfile.name) { - throw new HttpErrors.Unauthorized( - `Error generating token : userProfile 'name' is null`, - ); - } - const userInfoForToken = { id: userProfile.id, - email: userProfile.email, - name: userProfile.name, }; // Generate a JSON Web Token diff --git a/packages/authentication/src/__tests__/fixtures/strategies/basic-strategy.ts b/packages/authentication/src/__tests__/fixtures/strategies/basic-strategy.ts index d8b20d86deac..8263e151098a 100644 --- a/packages/authentication/src/__tests__/fixtures/strategies/basic-strategy.ts +++ b/packages/authentication/src/__tests__/fixtures/strategies/basic-strategy.ts @@ -10,7 +10,7 @@ import {BasicAuthenticationStrategyBindings} from '../keys'; import {BasicAuthenticationUserService} from '../services/basic-auth-user-service'; export interface BasicAuthenticationStrategyCredentials { - email: string; + username: string; password: string; } @@ -54,7 +54,7 @@ export class BasicAuthenticationStrategy implements AuthenticationStrategy { ); const encryptedCredentails = parts[1]; - // decrypt the credentials. Should look like : 'user_email_value:user_password_value' + // decrypt the credentials. Should look like : 'username:password' const decryptedCredentails = Buffer.from( encryptedCredentails, 'base64', @@ -70,7 +70,7 @@ export class BasicAuthenticationStrategy implements AuthenticationStrategy { } const creds: BasicAuthenticationStrategyCredentials = { - email: decryptedParts[0], + username: decryptedParts[0], password: decryptedParts[1], }; diff --git a/packages/authentication/src/__tests__/fixtures/users/user.repository.ts b/packages/authentication/src/__tests__/fixtures/users/user.repository.ts index ab14bb8bba58..efb94f8db618 100644 --- a/packages/authentication/src/__tests__/fixtures/users/user.repository.ts +++ b/packages/authentication/src/__tests__/fixtures/users/user.repository.ts @@ -7,9 +7,9 @@ import {User} from './user'; export class UserRepository { constructor(readonly list: {[key: string]: User}) {} - find(email: string): User | undefined { + find(username: string): User | undefined { const found = Object.keys(this.list).find( - k => this.list[k].email === email, + k => this.list[k].username === username, ); return found ? this.list[found] : undefined; } diff --git a/packages/authentication/src/__tests__/fixtures/users/user.ts b/packages/authentication/src/__tests__/fixtures/users/user.ts index 74f25fc77bb7..2a5373a3a9ff 100644 --- a/packages/authentication/src/__tests__/fixtures/users/user.ts +++ b/packages/authentication/src/__tests__/fixtures/users/user.ts @@ -5,7 +5,7 @@ export interface User { id: string; - email: string; + username: string; password: string; firstname?: string; surname?: string;