Hello! I’d like to create my own JWT authorizer. I...
# help
j
Hello! I’d like to create my own JWT authorizer. I don’t want to use AWS Cognito or Auth0. Is it possible? Is there some documentation about that?
s
Where is your token being issued from?
j
I will create my own API to issue the token. Does it make sense?
s
Yeah. It might be easier to create a custom lambda authoriser and then validate the token yourself.
j
Do you know any tutorial explaining how to do that?
s
I would search for two things: • how to verify a JWT token • how to define a custom lambda authoriser via CDK
You might get some luck searching this slack for "authorizer"
Here is a basic example of the SST/CDK code:
Copy code
import * as apigAuthorizers from "@aws-cdk/aws-apigatewayv2-authorizers-alpha";
import * as sst from "@serverless-stack/resources";

// The lambda function which will handle the authorisation checks:
const authorizerHandler = new sst.Function(this, "authorizer-lambda", {
  handler: "lambdas/custom-authorizer.handler",
});

// Initialises the custom lambda authoriser for use against our API Gateway:
const httpLambdaAuthorizer = new apigAuthorizers.HttpLambdaAuthorizer(
  "apigw-lambda-authorizer",
  authorizerHandler,
  {
    authorizerName: "LambdaAuthorizer",
    responseTypes: [apigAuthorizers.HttpLambdaResponseType.SIMPLE],
  },
);

// Declare an API Gateway V2 API:
const api = new sst.Api(this, "my-api", {
  // Our custom lambda authoriser will be used as the default for functions
  // that have an authorizationType configured
  defaultAuthorizer: httpLambdaAuthorizer,
  routes: {
    "GET /private": {
      // We set an authorization type on this route:
      authorizationType: sst.ApiAuthorizationType.CUSTOM,
      function: "api/private.handler",
    },
    // The following route will not be protected by our custom authorizer:
    "GET /public": "api/public.handler",
  },
});
And here is a very naive implementation for the authorizer lambda..
Copy code
import jwt from "jsonwebtoken";

export const handler = async (event) => {
  const [token] = event.identitySource;

  if (token == null || token === "") {
    <http://console.info|console.info>("No token provided. Access denied.");
    return {
      isAuthorized: false,
    };
  }

  try {
    jwt.verify(token, "my-token-secret");
  } catch (err) {
    return {
      isAuthorized: false,
    };
  }

  return {
    isAuthorized: true,
  };
};
j
Thank you so much, man!!!
t
I'd also throw in there if you're writing your own logic, it might make sense to just do it in your lambda handler. Otherwise you're incurring latency + cold start issues for little benefit
Unless you truly want to isolate the underlying resources
a
Code snippet in a Websocket handler Lambda that validates a JWT:
Copy code
import { decodeVerifyJwt } from "./decodeVerify";
import { APIGatewayProxyEvent } from "aws-lambda";
import { HttpError } from "http-errors";
import { WebSocketSubscribeAction } from "@pkg/models";

export const handler = async (event: APIGatewayProxyEvent) => {
  try {
    const action = JSON.parse(event.body!) as WebSocketSubscribeAction;
    const claims = await decodeVerifyJwt(action.authorization);
    if (!claims.isValid) {
      log.warnObject("Invalid JWT ", claims.error);
      return {
        statusCode: 403, // Forbidden
        body: JSON.stringify(claims.error),
      };
    }
(A member of my team wrote that.) Looks like the imported decodeVerifyJwt is based on this: https://github.com/awslabs/aws-support-tools/blob/master/Cognito/decode-verify-jwt/decode-verify-jwt.ts
d
Just to throw out there, you can also just use AWS's JWT authorizer, and set the issuer to your API. It will work so long as the public key matches. This is actually how it works with Cognito, you just set the issuer to Cognito's url.
a
☝️ Note, requires API Gateway v2 HTTP, not v1 REST. Ideal choice if you can use it. 👍