Skip to content

Commit 6fe845e

Browse files
committed
Sorting out the split between login and verify-email
1 parent 0efc10e commit 6fe845e

25 files changed

+432
-353
lines changed

src/authentication/auth-routes.ts

Lines changed: 6 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,9 @@
11
import {Dependencies} from '../dependencies';
22
import {Config} from '../configuration';
3-
import {html, Safe, safe} from '../types/html';
4-
import {Route, get, post} from '../types/route';
5-
import {
6-
auth,
7-
landing,
8-
callback,
9-
invalidLink,
10-
logIn,
11-
logOut,
12-
verifyEmailCallback,
13-
} from './handlers';
3+
import {Safe, safe} from '../types/html';
4+
import {Route} from '../types/route';
5+
import { routes as verifyEmailRoutes } from './verify-email/routes';
6+
import { loginRoutes } from './login/routes';
147

158
export const logInPath: Safe = safe('/log-in');
169
const invalidLinkPath = '/auth/invalid-magic-link';
@@ -21,25 +14,7 @@ export const authRoutes = (
2114
conf: Config
2215
): ReadonlyArray<Route> => {
2316
return [
24-
get(logInPath, logIn(deps)),
25-
get('/log-out', logOut),
26-
post('/auth', auth),
27-
get('/auth', (_req, res) => res.redirect(logInPath)),
28-
get('/auth/landing', landing('/auth/callback')),
29-
get('/auth/callback', callback(invalidLinkPath)),
30-
get(invalidLinkPath, invalidLink(logInPath)),
31-
get('/auth/verify-email/landing', landing('/auth/verify-email/callback')),
32-
get(
33-
'/auth/verify-email/callback',
34-
verifyEmailCallback(deps, conf, invalidVerificationLinkPath)
35-
),
36-
get(
37-
invalidVerificationLinkPath,
38-
invalidLink(
39-
logInPath,
40-
html`The verification link you have used is (no longer) valid. Go
41-
back to the <a href=${logInPath}>log in</a> page.`
42-
)
43-
),
17+
...loginRoutes(deps, conf),
18+
...verifyEmailRoutes(deps, conf),
4419
];
4520
};
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import jwt from 'jsonwebtoken';
2+
import {Config} from '../configuration';
3+
4+
export const createSignedToken =
5+
(conf: Config) =>
6+
(payload: object): string =>
7+
jwt.sign(payload, conf.TOKEN_SECRET, {expiresIn: '10m'});

src/authentication/handlers.ts

Lines changed: 0 additions & 156 deletions
This file was deleted.

src/authentication/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
export {magicLink} from './magic-link';
1+
export {magicLink} from './login/magic-link';
22
export {authRoutes} from './auth-routes';
3-
export {getUserFromSession} from './get-user-from-session';
4-
export {startMagicLinkEmailPubSub} from './start-magic-link-email-pub-sub';
3+
export {getUserFromSession} from './login/get-user-from-session';
4+
export {startMagicLinkEmailPubSub} from './login/start-magic-link-email-pub-sub';
55
export {sessionOptions as sessionConfig} from './session-config';
66
export {cookieSessionPassportWorkaround} from './cookie-session-passport-workaround';

src/authentication/check-your-mail.ts renamed to src/authentication/login/check-your-mail.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import {pipe} from 'fp-ts/lib/function';
2-
import {isolatedPageTemplate} from '../templates/page-template';
3-
import {html, safe, sanitizeString} from '../types/html';
4-
import {EmailAddress} from '../types';
5-
import {normaliseEmailAddress} from '../read-models/shared-state/normalise-email-address';
2+
import {isolatedPageTemplate} from '../../templates/page-template';
3+
import {html, safe, sanitizeString} from '../../types/html';
4+
import {EmailAddress} from '../../types';
5+
import {normaliseEmailAddress} from '../../read-models/shared-state/normalise-email-address';
66

77
export const checkYourMailPage = (submittedEmailAddress: EmailAddress) =>
88
pipe(

src/authentication/get-user-from-session.ts renamed to src/authentication/login/get-user-from-session.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import {flow, pipe} from 'fp-ts/lib/function';
22
import * as t from 'io-ts';
3-
import {User} from '../types';
3+
import {User} from '../../types';
44
import * as E from 'fp-ts/Either';
55
import * as O from 'fp-ts/Option';
6-
import {Dependencies} from '../dependencies';
6+
import {Dependencies} from '../../dependencies';
77
import {formatValidationErrors} from 'io-ts-reporters';
88

99
const SessionCodec = t.strict({
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import {RequestHandler} from 'express';
2+
import {Request, Response} from 'express';
3+
import {pipe} from 'fp-ts/lib/function';
4+
import {parseEmailAddressFromBody} from './parse-email-address-from-body';
5+
import * as E from 'fp-ts/Either';
6+
import * as O from 'fp-ts/Option';
7+
import {publish} from 'pubsub-js';
8+
import passport from 'passport';
9+
import {magicLink} from './magic-link';
10+
import {logInPage} from './log-in-page';
11+
import {checkYourMailPage} from './check-your-mail';
12+
import {oopsPage, isolatedPageTemplate} from '../../templates';
13+
import {StatusCodes} from 'http-status-codes';
14+
import {getUserFromSession} from './get-user-from-session';
15+
import {Dependencies} from '../../dependencies';
16+
import {
17+
html,
18+
HtmlSubstitution,
19+
CompleteHtmlDocument,
20+
sanitizeString,
21+
safe,
22+
} from '../../types/html';
23+
24+
export const logIn =
25+
(deps: Dependencies) =>
26+
(req: Request, res: Response<CompleteHtmlDocument>) => {
27+
pipe(
28+
req.session,
29+
getUserFromSession(deps),
30+
O.match(
31+
() => {
32+
res.status(StatusCodes.OK).send(logInPage);
33+
},
34+
_user => res.redirect('/')
35+
)
36+
);
37+
};
38+
39+
export const logOut = (req: Request, res: Response<CompleteHtmlDocument>) => {
40+
req.session = null;
41+
res.redirect('/');
42+
};
43+
44+
export const auth = (req: Request, res: Response<CompleteHtmlDocument>) => {
45+
pipe(
46+
req.body,
47+
parseEmailAddressFromBody,
48+
E.mapLeft(() => "You entered something that isn't a valid email address"),
49+
E.matchW(
50+
msg =>
51+
res.status(StatusCodes.BAD_REQUEST).send(oopsPage(sanitizeString(msg))),
52+
email => {
53+
publish('send-log-in-link', email);
54+
res.status(StatusCodes.ACCEPTED).send(checkYourMailPage(email));
55+
}
56+
)
57+
);
58+
};
59+
60+
export const landing = (req: Request, res: Response<CompleteHtmlDocument>) => {
61+
const index = req.originalUrl.indexOf('?');
62+
const suffix = index === -1 ? '' : req.originalUrl.slice(index);
63+
const url = '/auth/callback' + suffix;
64+
res.status(StatusCodes.OK).send(
65+
isolatedPageTemplate(sanitizeString('Redirecting...'))(html`
66+
<!doctype html>
67+
<html>
68+
<head>
69+
<meta http-equiv="refresh" content="0; url='${safe(url)}'" />
70+
</head>
71+
<body></body>
72+
</html>
73+
`)
74+
);
75+
};
76+
77+
export const callback = (invalidLinkPath: string) =>
78+
passport.authenticate(magicLink.name, {
79+
failureRedirect: invalidLinkPath,
80+
successRedirect: '/',
81+
}) as RequestHandler;
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import {Request, Response} from 'express';
2+
import {oopsPage} from '../../templates';
3+
import {StatusCodes} from 'http-status-codes';
4+
import {
5+
html,
6+
HtmlSubstitution,
7+
CompleteHtmlDocument,
8+
} from '../../types/html';
9+
10+
11+
export const invalidLink =
12+
(logInPath: HtmlSubstitution) =>
13+
(req: Request, res: Response<CompleteHtmlDocument>) => {
14+
res
15+
.status(StatusCodes.UNAUTHORIZED)
16+
.send(
17+
oopsPage(
18+
html`The link you have used is (no longer) valid. Go back to the
19+
<a href=${logInPath}>log in</a> page.`
20+
)
21+
);
22+
};
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {pipe} from 'fp-ts/lib/function';
2-
import {html, safe} from '../types/html';
3-
import {isolatedPageTemplate} from '../templates/page-template';
2+
import {html, safe} from '../../types/html';
3+
import {isolatedPageTemplate} from '../../templates/page-template';
44

55
export const logInPage = pipe(
66
html`

0 commit comments

Comments
 (0)