docs/cookbook/authentication/apiKey.md
# API Key Authentication
We will start by providing the required configuration for this strategy. You should change all of these values as per your requirement.
```js
{
"authentication": {
...otherConfig,
"authStrategies": [ ...otherStrategies, "apiKey" ],
"apiKey": {
"allowedKeys": [ "API_KEY_1", "API_KEY_2" ],
"header": "x-access-token"
}
}
}
```
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",
"port": 3030,
"public": "../public/",
"paginate": {
"default": 10,
"max": 50
},
"authentication": {
"secret": "some-secret",
"service": "users",
"entity": null,
"authStrategies": ["apiKey"],
"apiKey": {
"allowedKeys": [ "API_KEY_1", "API_KEY_2" ],
"header": "x-access-token"
}
}
}
```
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';
declare module './declarations' {
interface ServiceTypes {
'authentication': AuthenticationService & ServiceAddons<any>;
}
}
class ApiKeyStrategy extends AuthenticationBaseStrategy {
app: Application;
constructor(app: Application) {
super();
this.app = app;
}
async authenticate(authentication: AuthenticationResult) {
const { token } = authentication;
const config = this.app.get('authentication').apiKey;
const match = config.allowedKeys.includes(token);
if (!match) throw new NotAuthenticated('Incorrect API Key');
return {
apiKey: true
}
}
}
export default function (app: Application) {
const authentication = new AuthenticationService(app);
// This can have multiple .register calls if multiple strategies have been added
authentication.register('apiKey', new ApiKeyStrategy(app));
app.use('/authentication', authentication);
}
```
</LanguageBlock>
<LanguageBlock global-id="js">
In `src/authentication.js`:
```js
const { AuthenticationBaseStrategy, AuthenticationService } = require('@feathersjs/authentication');
const { NotAuthenticated } = require('@feathersjs/errors');
class ApiKeyStrategy extends AuthenticationBaseStrategy {
async authenticate(authentication) {
const { token } = authentication;
const config = this.authentication.configuration[this.name];
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);
// ... authentication service setup
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';
export default () => async (context: HookContext, next: NextFunction) => {
const { params, app } = context;
const headerField = app.get('authentication').apiKey.header;
const token = params.headers ? params.headers[headerField] : null;
if (token && params.provider && !params.authentication) {
context.params = {
...params,
authentication: {
strategy: 'apiKey',
token
}
};
}
return next();
}
```
</LanguageBlock>
<LanguageBlock global-id="js">
```js
/* eslint-disable require-atomic-updates */
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];
if (token && params.provider && !params.authentication) {
context.params = {
...params,
authentication: {
strategy: 'apiKey',
token
}
};
}
return context;
};
};
```
</LanguageBlock>
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 allowApiKey from './hooks/allow-api-key';
all: [ allowApiKey(), authenticate('jwt', 'apiKey') ],
```
If a user now accesses the service externally with the correct apiKey, the service call will succeed and have `params.apiKey` set to `true`.