Skip to content

Commit

Permalink
authentication ES modules
Browse files Browse the repository at this point in the history
  • Loading branch information
daffl committed Dec 22, 2023
1 parent eab04b9 commit 4ca1ab6
Show file tree
Hide file tree
Showing 11 changed files with 138 additions and 88 deletions.
99 changes: 48 additions & 51 deletions docs/cookbook/authentication/apiKey.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ We will start by providing the required configuration for this strategy. You sho
Note: if all you want is api key authentication, it is still necessary to register a secret, service and entity. Since no other authentication method is used, entity can be `null`.

A fully working example with just API key authentication:

```json
{
"host": "localhost",
Expand All @@ -33,7 +34,7 @@ A fully working example with just API key authentication:
"entity": null,
"authStrategies": ["apiKey"],
"apiKey": {
"allowedKeys": [ "API_KEY_1", "API_KEY_2" ],
"allowedKeys": ["API_KEY_1", "API_KEY_2"],
"header": "x-access-token"
}
}
Expand All @@ -42,38 +43,39 @@ A fully working example with just API key authentication:

Next we will be creating a [custom strategy](../../api/authentication/strategy.md) that returns the `params` that you would like to use to identify an authenticated user/request.



<LanguageBlock global-id="ts">

```ts
import { AuthenticationBaseStrategy, AuthenticationResult, AuthenticationService } from '@feathersjs/authentication';
import { NotAuthenticated } from '@feathersjs/errors';
import { ServiceAddons } from '@feathersjs/feathers';
import { Application } from './declarations';

import {
AuthenticationBaseStrategy,
AuthenticationResult,
AuthenticationService
} from '@feathersjs/authentication'
import { NotAuthenticated } from '@feathersjs/errors'
import { ServiceAddons } from '@feathersjs/feathers'
import { Application } from './declarations'

declare module './declarations' {
interface ServiceTypes {
'authentication': AuthenticationService & ServiceAddons<any>;
authentication: AuthenticationService & ServiceAddons<any>
}
}

class ApiKeyStrategy extends AuthenticationBaseStrategy {
app: Application;
app: Application

constructor(app: Application) {
super();
this.app = app;
super()
this.app = app
}

async authenticate(authentication: AuthenticationResult) {
const { token } = authentication;
const { token } = authentication

const config = this.app.get('authentication').apiKey;
const config = this.app.get('authentication').apiKey

const match = config.allowedKeys.includes(token);
if (!match) throw new NotAuthenticated('Incorrect API Key');
const match = config.allowedKeys.includes(token)
if (!match) throw new NotAuthenticated('Incorrect API Key')

return {
apiKey: true
Expand All @@ -82,12 +84,12 @@ class ApiKeyStrategy extends AuthenticationBaseStrategy {
}

export default function (app: Application) {
const authentication = new AuthenticationService(app);
const authentication = new AuthenticationService(app)

// This can have multiple .register calls if multiple strategies have been added
authentication.register('apiKey', new ApiKeyStrategy(app));
authentication.register('apiKey', new ApiKeyStrategy(app))

app.use('/authentication', authentication);
app.use('/authentication', authentication)
}
```

Expand All @@ -98,49 +100,45 @@ export default function (app: Application) {
In `src/authentication.js`:

```js
const { AuthenticationBaseStrategy, AuthenticationService } = require('@feathersjs/authentication');
const { NotAuthenticated } = require('@feathersjs/errors');
const { AuthenticationBaseStrategy, AuthenticationService } = require('@feathersjs/authentication')
const { NotAuthenticated } = require('@feathersjs/errors')

class ApiKeyStrategy extends AuthenticationBaseStrategy {
async authenticate(authentication) {
const { token } = authentication;
const { token } = authentication

const config = this.authentication.configuration[this.name];
const config = this.authentication.configuration[this.name]

const match = config.allowedKeys.includes(token);
if (!match) throw new NotAuthenticated('Incorrect API Key');
const match = config.allowedKeys.includes(token)
if (!match) throw new NotAuthenticated('Incorrect API Key')

return {
apiKey: true
}
}
}

module.exports = app => {
const authentication = new AuthenticationService(app);
module.exports = (app) => {
const authentication = new AuthenticationService(app)
// ... authentication service setup
authentication.register('apiKey', new ApiKeyStrategy());
authentication.register('apiKey', new ApiKeyStrategy())
}
```

</LanguageBlock>



Next, we create a hook called `allow-apiKey` that sets `params.authentication` if it does not exist and if `params.provider` exists (which means it is an external call) to use that `apiKey` strategy. We will also provide the capability for the apiKey to be read from the request header: (you could also read the token as a query parameter but you will have to filter it out before it's passed to Feathers calls like `get` and `find`.



<LanguageBlock global-id="ts">

```ts
import { HookContext, NextFunction } from '@feathersjs/feathers';
import { HookContext, NextFunction } from '@feathersjs/feathers'

export default () => async (context: HookContext, next: NextFunction) => {
const { params, app } = context;
const { params, app } = context

const headerField = app.get('authentication').apiKey.header;
const token = params.headers ? params.headers[headerField] : null;
const headerField = app.get('authentication').apiKey.header
const token = params.headers ? params.headers[headerField] : null

if (token && params.provider && !params.authentication) {
context.params = {
Expand All @@ -149,10 +147,10 @@ export default () => async (context: HookContext, next: NextFunction) => {
strategy: 'apiKey',
token
}
};
}
}

return next();
return next()
}
```

Expand All @@ -162,12 +160,13 @@ export default () => async (context: HookContext, next: NextFunction) => {

```js
/* eslint-disable require-atomic-updates */
module.exports = function (options = {}) { // eslint-disable-line no-unused-vars
return async context => {
const { params, app } = context;
module.exports = function (options = {}) {
// eslint-disable-line no-unused-vars
return async (context) => {
const { params, app } = context

const headerField = app.get('authentication').apiKey.header;
const token = params.headers[headerField];
const headerField = app.get('authentication').apiKey.header
const token = params.headers[headerField]

if (token && params.provider && !params.authentication) {
context.params = {
Expand All @@ -176,22 +175,20 @@ module.exports = function (options = {}) { // eslint-disable-line no-unused-vars
strategy: 'apiKey',
token
}
};
}
}

return context;
};
};
return context
}
}
```

</LanguageBlock>



This hook should be added __before__ the [authenticate hook](../../api/authentication/hook.md) wherever API Key authentication should be allowed:
This hook should be added **before** the [authenticate hook](../../api/authentication/hook.md) wherever API Key authentication should be allowed:

```js
import { authenticate } from '@feathersjs/authentication/lib/hooks';
import { authenticate } from '@feathersjs/authentication';
import allowApiKey from './hooks/allow-api-key';

all: [ allowApiKey(), authenticate('jwt', 'apiKey') ],
Expand Down
26 changes: 19 additions & 7 deletions packages/authentication-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
"description": "The authentication plugin for feathers-client",
"version": "5.0.12",
"homepage": "https://feathersjs.com",
"main": "lib/",
"types": "lib/",
"main": "./lib/index.js",
"types": "./types/index.d.ts",
"keywords": [
"feathers",
"feathers-plugin"
Expand All @@ -29,22 +29,26 @@
"url": "https://github.com/feathersjs/feathers/issues"
},
"engines": {
"node": ">= 12"
"node": ">= 18"
},
"files": [
"CHANGELOG.md",
"LICENSE",
"README.md",
"src/**",
"lib/**",
"types/**",
"esm/**",
"*.d.ts",
"*.js"
],
"scripts": {
"prepublish": "npm run compile",
"pack": "npm pack --pack-destination ../generators/test/build",
"compile": "shx rm -rf lib/ && tsc && npm run pack",
"test": "mocha --config ../../.mocharc.json --recursive test/**.test.ts test/**/*.test.ts"
"compile": "npm run compile:cjs && npm run compile:esm && npm run pack",
"test": "mocha --config ../../.mocharc.json --recursive test/**.test.ts test/**/*.test.ts",
"compile:esm": "shx rm -rf esm/ && tsc --outDir esm/ --module esnext && shx echo '{ \"type\": \"module\" }' > esm/package.json",
"compile:cjs": "npx shx rm -rf types/ lib/ && tsc && shx echo '{ \"type\": \"commonjs\" }' > lib/package.json"
},
"directories": {
"lib": "lib"
Expand Down Expand Up @@ -73,5 +77,13 @@
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
},
"gitHead": "90caf635aec850550b9d37bea2762af959d9e8d5"
}
"gitHead": "90caf635aec850550b9d37bea2762af959d9e8d5",
"module": "./esm/index.js",
"exports": {
".": {
"import": "./esm/index.js",
"require": "./lib/index.js",
"types": "./types/index.d.ts"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import socketioClient from '@feathersjs/socketio-client'
import authClient from '../../src/index'
import getApp from './fixture'
import commonTests from './commons'
import { AuthenticationResult } from '@feathersjs/authentication/lib'
import { AuthenticationResult } from '@feathersjs/authentication'

describe('@feathersjs/authentication-client Socket.io integration', () => {
let app: Application
Expand Down
5 changes: 3 additions & 2 deletions packages/authentication-client/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"src/**/*.ts"
],
"compilerOptions": {
"outDir": "lib"
"declarationDir": "./types",
"outDir": "lib"
}
}
}
25 changes: 19 additions & 6 deletions packages/authentication-local/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
"description": "Local authentication strategy for @feathers/authentication",
"version": "5.0.12",
"homepage": "https://feathersjs.com",
"main": "lib/",
"types": "lib/",
"main": "./lib/index.js",
"types": "./types/index.d.ts",
"keywords": [
"feathers",
"feathers-plugin"
Expand All @@ -29,22 +29,26 @@
"url": "https://github.com/feathersjs/feathers/issues"
},
"engines": {
"node": ">= 12"
"node": ">= 18"
},
"files": [
"CHANGELOG.md",
"LICENSE",
"README.md",
"src/**",
"lib/**",
"types/**",
"esm/**",
"*.d.ts",
"*.js"
],
"scripts": {
"prepublish": "npm run compile",
"pack": "npm pack --pack-destination ../generators/test/build",
"compile": "shx rm -rf lib/ && tsc && npm run pack",
"test": "mocha --config ../../.mocharc.json --recursive test/**.test.ts test/**/*.test.ts"
"compile": "npm run compile:cjs && npm run compile:esm && npm run pack",
"test": "mocha --config ../../.mocharc.json --recursive test/**.test.ts test/**/*.test.ts",
"compile:esm": "shx rm -rf esm/ && tsc --outDir esm/ --module esnext && shx echo '{ \"type\": \"module\" }' > esm/package.json",
"compile:cjs": "npx shx rm -rf types/ lib/ && tsc && shx echo '{ \"type\": \"commonjs\" }' > lib/package.json"
},
"directories": {
"lib": "lib"
Expand Down Expand Up @@ -72,5 +76,14 @@
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
},
"gitHead": "90caf635aec850550b9d37bea2762af959d9e8d5"
"gitHead": "90caf635aec850550b9d37bea2762af959d9e8d5",
"module": "./esm/index.js",
"exports": {
".": {
"import": "./esm/index.js",
"require": "./lib/index.js",
"types": "./types/index.d.ts"
},
"./test/fixture": "./test/fixture.ts"
}
}
5 changes: 3 additions & 2 deletions packages/authentication-local/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"src/**/*.ts"
],
"compilerOptions": {
"outDir": "lib"
"declarationDir": "./types",
"outDir": "lib"
}
}
}
Loading

0 comments on commit 4ca1ab6

Please sign in to comment.