auth0-lambda-authorizer
Version:
An Auth0 Authorizer for AWS Lambda
98 lines (79 loc) • 3.14 kB
Markdown
is a simple custom authorizer for AWS lambda.
- jwks-rsa
When writing FaaS applications on AWS lambda that
have an `http` endpoint, we sometimes want to
secure these endpoints and only allow authenticated
and authorized users to access them. We use `JWT` for
this and for the past FaaS projects we have just copy
pasted this custom authorizer across projects. Extracting
it into a library will give us the ability to share
improvements and bugs across projects without having
to copy paste the code around.
We need to first create a `jwksClient` using `jwks-rsa`.
```typescript
const client = jwksClient({
cache: true,
jwksRequestsPerMinute: 10,
jwksUri: process.env.JWKS_URI,
rateLimit: true,
});
```
The `JWKS_URI` should be fetched from the `Auth0` dashboard.
Here we have added it as an environment variable.
We also need to decode the auth0 secret. The approach we normally
take is to save the `PUBLIC_PEM` using `kms` and then decrypt it,
and convert it to a Buffer.
```typescript
//Given a function for decrypting a KMS string:
const encodedString = await getDecryptedKmsString(process.env.AUTH0_WEB_PUBLIC_PEM);
const auth0Secret = new Buffer(encodedSecret, "base64").toString();
```
Then we need to create a callback that will be executed once
we have decoded the token. This callback is also responsible
for returning control to the lambda environment by calling `context.succeed`
or `context.fail`:
```typescript
const authenticateCallback = (err, authResponse: AuthResponse) => {
if (err) {
if (!err) {
log.error("Failed to authenticate with an unhandled error", err);
context.fail("Unauthorized");
} else {
log.error("Recieved an unauthorized request", err);
context.fail("Unauthorized");
}
} else {
log.info({ msg: "Successfully authenticated a request" });
context.succeed(authResponse);
}
}
```
This callback will get either an `error` or a response from our `authenticate` function.
AuthResponse looks like this:
```typescript
export interface AuthResponse {
policyDocument: PolicyDocument;
principalId: string;
context: any;
}
```
This includes a `policyDocument`, the `principalId` and the `context`.
The `context` is a map that will contain the `payload` along with the scope.
A default `policyDocument` is created that allows access to execute
any lambda. However, we might not always want to do this.
We can create our own `policyDocument` if we want more fine grained control
by using the `getPolicyDocument` function.
```typescript
//given that we have imported `ramda` as R.
if (R.contains("admin", context.roles) || R.contains("premiumUser", context.roles)) {
const policyDocument = getPolicyDocument("Allow", [event.methodArn])
authResponse.policyDocument = policyDocument;
} else {
const policyDocument = getPolicyDocument("Deny", [event.methodArn])
authResponse.policyDocument = policyDocument;
}
```
This