examples icon indicating copy to clipboard operation
examples copied to clipboard

Websocket authorzation handler - return something that isn't `500`?

Open KATT opened this issue 4 years ago • 1 comments

Question / help needed

I'm trying to get authorization working before connecting to websockets, which works, but I'm struggling to return an error message that isn't 500.

I've managed to make an auth handler, like so:


    connectHandler: {
      handler: 'handler.connectHandler',
      events: [
        {
          "websocket": {
            route: "$connect",
            authorizer: {
              name: "authHandler",
              identitySource: [
                // No identity source, leave it up to the handler
              ],
            },
          },
        },
      ],
      environment: {
        // ..
      },
    },

Any my authHandler:

export const authHandler = async (event, _context) => {
  console.log('🔑 Auth request received', event.requestContext)

  const token = event.headers.Authorization ?? event.queryStringParameters.Authorization;
  console.log('Token:', token)

  if (token !== 'secret') {
    return {
      statusCode: 401,
    }
  }

  return {
    "principalId": "user",
    "policyDocument": {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Action": "execute-api:Invoke",
          "Effect": "Allow",
          "Resource": event.methodArn
        }
      ]
    }
  };
}

Questions:

  • what should I return in case of an error? Any request to my websocket-server that doesn't have the secret header/querystring returns 500 right now, and I'd prefer it to return a 4xx status code.

  • what is the correct type for the authorization handler? I'm using APIGatewayProxyHandler on the other fn handlers which gives me nice inline errors.

    How my other handlers look like, which gives me pleasant TypeScript-errors when I do something wrong ```ts

    export const connectHandler: APIGatewayProxyHandler = async (event, _context) => { const connectionId = event.requestContext.connectionId!

    console.log('➕ Adding connection ', connectionId)

    await client.query('INSERT INTO "public"."Connection"("id") VALUES($1) RETURNING *', [connectionId]) return { statusCode: 200, body: '', }; }

    </details>
    

KATT avatar Sep 27 '20 14:09 KATT

I managed do get 4xx working by letting the user "slip through" the auth handler, which doesn't feel great, but it works --

If the connect-handler returns 401 it actually propagates to the connecting client

type AuthContext = {
  authorized: '0'; // this would be a `boolean`, but seems like AWS stringifies boolean values of the ctx
} | {
  authorized: '1';
  token: string;
}

export const connectHandler: APIGatewayProxyWithLambdaAuthorizerHandler<AuthContext> = async (event, _context) => {
  const connectionId = event.requestContext.connectionId!
  if (event.requestContext.authorizer.authorized !== '1') {
    return {
      statusCode: 401,
      body: ''
    }
  }

  console.log('➕ Adding connection ', connectionId)

  await client.query('INSERT INTO "public"."Connection"("id") VALUES($1) RETURNING *', [connectionId])
  return {
    statusCode: 200,
    body: '',
  };
}

export const authHandler: APIGatewayRequestAuthorizerWithContextHandler<AuthContext> = async (event, _context) => {
  console.log('🔑 Auth request received', event.requestContext)

  const token = event.headers.Authorization ?? event.queryStringParameters.Authorization;
  console.log('Token:', token)

  const result = {
    "principalId": "user",
    "policyDocument": {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Action": "execute-api:Invoke",
          "Effect": "Allow",
          "Resource": '*'
        }
      ]
    }
  }
  if (token !== 'secret') {
    return {
      ...result,
      context: {
        authorized: '0',
      }
    };
  }

  return {
    ...result,
    context: {
      authorized: '1',
      token,
    }
  };
}

KATT avatar Sep 27 '20 15:09 KATT