1 | # S3O Lambda
|
2 |
|
3 | This library makes it a lot easier & faster to secure the HTTP endpoints of AWS Lambda-based serverless applications to FT staff only.
|
4 |
|
5 | For an equivalent for [Express](https://expressjs.com/) based services, see [s3o-middleware](https://github.com/Financial-Times/s3o-middleware).
|
6 |
|
7 | ## Installation
|
8 |
|
9 | ```sh
|
10 | npm install --save @financial-times/s3o-lambda
|
11 | ```
|
12 |
|
13 | ## Usage
|
14 |
|
15 | The simplest setup consists of the following:
|
16 |
|
17 | ```js
|
18 | const s3o = require('@financial-times/s3o-lambda');
|
19 |
|
20 | module.exports.handler = (event, context, callback) => {
|
21 | s3o(event, callback).then(({ isSignedIn, username }) => {
|
22 | if (isSignedIn) {
|
23 | // your apps logic and eventual call of 'callback'
|
24 | callback(null, { statusCode: 200, body: 'You are signed in.' });
|
25 | }
|
26 | // do not call callback if the user is isSignedIn is false
|
27 | // it has already been called by s3o-lambda
|
28 | });
|
29 | };
|
30 | ```
|
31 |
|
32 | If you wish to use a vanity URL (e.g `runbooks.ft.com` rather than `https://115e7cvz86.execute-api.eu-west-1.amazonaws.com/prod`), you may want to customise the `X-FT-Forwarding-Host` header passed to your Lambda, or customise the [getHost](<#getHost(event)>) option.
|
33 |
|
34 | ### API
|
35 |
|
36 | #### s3o(event, callback, [options])
|
37 |
|
38 | Returns a Promise which resolves to an object:
|
39 |
|
40 | - `isSignedIn`: `bool` indicates whether the user is signed in.
|
41 | - `username`: `string` | `undefined` the s3o-username of the user if it existed in a cookie.
|
42 |
|
43 | In the event that the user is not signed in the `callback` will be called to redirect the user to the correct location. This indicates the Lambda is ready to return a response to the user. The callback should not be called again in this instance, if it is it will be a no-op and produce an AWS Lambda warning.
|
44 |
|
45 | If the user has the correct cookies but validation of the cookies fails, they will receive a 401 response clearing their cookies, rather than a redirect. This is in order to prevent redirect loops if there is an issue with S3O.
|
46 |
|
47 | ##### Options
|
48 |
|
49 | ###### redirect
|
50 |
|
51 | This library will redirect the user to sign in using Google if not signed in, and return the user to this same page when successful.
|
52 |
|
53 | If you want to prevent the redirect behaviour, pass `{redirect: false}` as the third parameter:
|
54 |
|
55 | ```js
|
56 | const s3o = require('@financial-times/s3o-lambda');
|
57 |
|
58 | module.exports.handler = (event, context, callback) => {
|
59 | s3o(event, callback, { redirect: false }).then(({ isSignedIn }) => {
|
60 | if (isSignedIn) {
|
61 | callback(null, { statusCode: 200, body: 'You are signed in.' });
|
62 | }
|
63 | });
|
64 | };
|
65 | ```
|
66 |
|
67 | Unauthorised users will receive a 401 status code. It is then up to you to handle
|
68 | redirecting the user to the S3O service in your client-side app.
|
69 |
|
70 | ###### protocol
|
71 |
|
72 | Type: `string`
|
73 | Default: `https`
|
74 |
|
75 | To allow use with `http` (useful for local development), pass in `{protocol: 'http'}` as the third parameter.
|
76 |
|
77 | ###### cookies
|
78 |
|
79 | Type: `object`
|
80 |
|
81 | Cookie options:
|
82 |
|
83 | - httpOnly: `boolean` Sets the `HttpOnly` flag on S3O cookies. Default `true`
|
84 | - maxAge: `string` `int` Sets the `Max-Age` flag on S3O cookies. Default: `900000`
|
85 | - secure: `boolean` Sets the `Secure` flag on S3O cookies. Default `protocol === 'https'`
|
86 |
|
87 | ###### getHost(event)
|
88 |
|
89 | Type: `Function` `string`
|
90 |
|
91 | A function which returns a host to be used as a redirectURL when redirecting to S3O.
|
92 | A constant string can also be used if this is static for the given handler.
|
93 |
|
94 | In most cases you will want your Lambda handler to have a vanity URL, rather than the default execution URL.
|
95 | AWS API Gateway routes by the `Host` header, so it's difficult to retrieve the original header without setting a forwarding header. If the `Host` header is used, the user will be redirected to the orginal API Gateway URL rather than your vanity URL.
|
96 |
|
97 | Overrides are used in the following order:
|
98 |
|
99 | 1. A provided `getHost` function or string, see below.
|
100 | 2. The `X-FT-Forwarding-Host` header. This can be used as a generic override for any upstream proxy.
|
101 | 3. The `Fastly-Orig-Host` header. If using Fastly and an ['override host header'](https://docs.fastly.com/guides/basic-configuration/specifying-an-override-host) is set, the original header is propagated as `Fastly-Orig-Host`.
|
102 |
|
103 | If setting custom headers doesn't meet your requirements, it may be useful to pass a `getHost` function. e.g.
|
104 |
|
105 | ````js
|
106 | getHost(event) {
|
107 | return event.headers['Custom-Forwarded-Host-Header'];
|
108 | }
|
109 | ```
|
110 |
|
111 | As a string
|
112 |
|
113 | ```js
|
114 | // may wish to store this in an environment variable
|
115 | const getHost = 'https://runbooks.ft.com';
|
116 | ````
|
117 |
|
118 | Passing an invalid type will result in an immediate promise rejection by `s3o-lambda`.
|
119 |
|
120 | ###### getPath(event, host)
|
121 |
|
122 | Type: `Function` `string`
|
123 |
|
124 | A function which returns the URL path to be used as a redirectURL when redirecting to S3O.
|
125 | A constant string can also be used if this is static for the given handler.
|
126 | By default the path is calculated by:
|
127 |
|
128 | 1. Take the `X-FT-Forwarding-Path` header if it is provided.
|
129 | 2. If host is a default AWS Lambda URL - i.e. it contains amazonaws.com, take `event.path` and prepend the `stage` variable from `requestContext`.
|
130 | 3. Take the value of `event.path`.
|
131 |
|
132 | In most cases the default behaviour should not need to be modified.
|
133 |
|
134 | ```js
|
135 | getPath(event, host) {
|
136 | return '/v1/some-hardcoded-path';
|
137 | }
|
138 | ```
|
139 |
|
140 | Passing an invalid type will result in an immediate promise rejection by `s3o-lambda`.
|
141 |
|
142 | ###### getS3oClientName(event)
|
143 |
|
144 | Type: `Function` `string`
|
145 |
|
146 | At the time of writing, S3O tokens are irrevocable. To ensure that leaked s3o tokens cannot be re-used on other services an identifier must be passed to the S3O `/authorise` endpoint as a `host` query parameter.
|
147 |
|
148 | The default behaviour is to take the output of [getHost](<#getHost(event)>).
|
149 | This can be customised by passing a `getS3oClientName` function which takes the lambda `event` as an argument.
|
150 | A constant string can also be used if this is static for the given handler.
|
151 |
|
152 | ```js
|
153 | // function
|
154 | module.exports.handler = (event, context, callback) => {
|
155 | s3o(event, callback, {
|
156 | getIdentifier(event) {
|
157 | return event.headers.host + 'my-lambda';
|
158 | },
|
159 | }).then(({ isSignedIn }) => {
|
160 | if (isSignedIn) {
|
161 | callback(null, { statusCode: 200, body: 'You are signed in.' });
|
162 | }
|
163 | });
|
164 | };
|
165 | ```
|
166 |
|
167 | ```js
|
168 | // string
|
169 | module.exports.handler = (event, context, callback) => {
|
170 | s3o(event, callback, { getS3oClientName: 'my-lambda-function' }).then(
|
171 | ({ isSignedIn }) => {
|
172 | if (isSignedIn) {
|
173 | callback(null, { statusCode: 200, body: 'You are signed in.' });
|
174 | }
|
175 | }
|
176 | );
|
177 | };
|
178 | ```
|
179 |
|
180 | Passing an invalid type will result in an immediate promise rejection by `s3o-lambda`.
|