Skip to content

Commit

Permalink
fix(authentication-local): Local Auth - Nested username & Password fi…
Browse files Browse the repository at this point in the history
…elds (#3091)
  • Loading branch information
eXigentCoder committed Jun 7, 2023
1 parent 3742028 commit d135526
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 11 deletions.
4 changes: 2 additions & 2 deletions docs/api/authentication/local.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ export const authentication = (app: Application) => {

Options are set in the [authentication configuration](./service.md#configuration) under the strategy name. Available options are:

- `usernameField`: Name of the username field (e.g. `'email'`)
- `passwordField`: Name of the password field (e.g. `'password'`)
- `usernameField`: Name of the username field (e.g. `'email'`), may be a nested property (e.g. `'auth.email'`)
- `passwordField`: Name of the password field (e.g. `'password'`), may be a nested property (e.g. `'auth.password'`)
- `hashSize` (default: `10`): The BCrypt salt length
- `errorMessage` (default: `'Invalid login'`): The error message to return on errors
- `entityUsernameField` (default: `usernameField`): Name of the username field on the entity if authentication request data and entity field names are different
Expand Down
5 changes: 2 additions & 3 deletions packages/authentication-local/src/strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,16 +122,15 @@ export class LocalStrategy extends AuthenticationBaseStrategy {

async authenticate(data: AuthenticationRequest, params: Params) {
const { passwordField, usernameField, entity, errorMessage } = this.configuration
const username = data[usernameField]
const password = data[passwordField]
const username = get(data, usernameField)
const password = get(data, passwordField)

if (!password) {
// exit early if there is no password
throw new NotAuthenticated(errorMessage)
}

const result = await this.findEntity(username, omit(params, 'provider'))

await this.comparePassword(result, password)

return {
Expand Down
17 changes: 11 additions & 6 deletions packages/authentication-local/test/fixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ export type ServiceTypes = {
users: MemoryService
}

export function createApplication(app = feathers<ServiceTypes>()) {
export function createApplication(
app = feathers<ServiceTypes>(),
authOptionOverrides: Record<string, unknown> = {}
) {
const authentication = new AuthenticationService(app)

app.set('authentication', {
const authConfig = {
entity: 'user',
service: 'users',
secret: 'supersecret',
Expand All @@ -22,8 +25,10 @@ export function createApplication(app = feathers<ServiceTypes>()) {
local: {
usernameField: 'email',
passwordField: 'password'
}
})
},
...authOptionOverrides
}
app.set('authentication', authConfig)

authentication.register('jwt', new JWTStrategy())
authentication.register('local', new LocalStrategy())
Expand All @@ -40,9 +45,9 @@ export function createApplication(app = feathers<ServiceTypes>()) {
})
)

app.service('users').hooks([protect('password')])
app.service('users').hooks([protect(authConfig.local.passwordField)])
app.service('users').hooks({
create: [hashPassword('password')],
create: [hashPassword(authConfig.local.passwordField)],
get: [
async (context, next) => {
await next()
Expand Down
26 changes: 26 additions & 0 deletions packages/authentication-local/test/strategy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,4 +195,30 @@ describe('@feathersjs/authentication-local/strategy', () => {

assert.notStrictEqual(resolvedData.password, 'supersecret')
})
it('should allow for nested values in the usernameField', async () => {
const appWithNestedFieldOverride = createApplication(undefined, {
local: {
usernameField: 'auth.email',
passwordField: 'auth.password'
}
})
const nestedUser = await appWithNestedFieldOverride.service('users').create({ auth: { email, password } })
const authService = appWithNestedFieldOverride.service('authentication')
const authResult = await authService.create({
strategy: 'local',
auth: {
email,
password
}
})
const { accessToken } = authResult

assert.ok(accessToken)
assert.strictEqual(authResult.user.auth.email, email)

const decoded = await authService.verifyAccessToken(accessToken)

assert.strictEqual(decoded.sub, `${nestedUser.id}`)
//
})
})

0 comments on commit d135526

Please sign in to comment.