feat: Add ctx.state.authType for tracking (#1567)

This commit is contained in:
Tom Moor
2020-09-21 22:02:37 -07:00
committed by GitHub
parent fa96891c8e
commit 0fa8a6ed2e
3 changed files with 39 additions and 25 deletions

View File

@@ -2,14 +2,17 @@
import addMinutes from "date-fns/add_minutes"; import addMinutes from "date-fns/add_minutes";
import addMonths from "date-fns/add_months"; import addMonths from "date-fns/add_months";
import JWT from "jsonwebtoken"; import JWT from "jsonwebtoken";
import { type Context } from "koa";
import { AuthenticationError, UserSuspendedError } from "../errors"; import { AuthenticationError, UserSuspendedError } from "../errors";
import { User, ApiKey } from "../models"; import { User, Team, ApiKey } from "../models";
import type { ContextWithState } from "../types";
import { getCookieDomain } from "../utils/domains"; import { getCookieDomain } from "../utils/domains";
import { getUserForJWT } from "../utils/jwt"; import { getUserForJWT } from "../utils/jwt";
export default function auth(options?: { required?: boolean } = {}) { export default function auth(options?: { required?: boolean } = {}) {
return async function authMiddleware(ctx: Context, next: () => Promise<*>) { return async function authMiddleware(
ctx: ContextWithState,
next: () => Promise<mixed>
) {
let token; let token;
const authorizationHeader = ctx.request.get("authorization"); const authorizationHeader = ctx.request.get("authorization");
@@ -27,7 +30,6 @@ export default function auth(options?: { required?: boolean } = {}) {
`Bad Authorization header format. Format is "Authorization: Bearer <token>"` `Bad Authorization header format. Format is "Authorization: Bearer <token>"`
); );
} }
// $FlowFixMe
} else if (ctx.body && ctx.body.token) { } else if (ctx.body && ctx.body.token) {
token = ctx.body.token; token = ctx.body.token;
} else if (ctx.request.query.token) { } else if (ctx.request.query.token) {
@@ -43,7 +45,8 @@ export default function auth(options?: { required?: boolean } = {}) {
let user; let user;
if (token) { if (token) {
if (String(token).match(/^[\w]{38}$/)) { if (String(token).match(/^[\w]{38}$/)) {
// API key ctx.state.authType = "api";
let apiKey; let apiKey;
try { try {
apiKey = await ApiKey.findOne({ apiKey = await ApiKey.findOne({
@@ -51,18 +54,22 @@ export default function auth(options?: { required?: boolean } = {}) {
secret: token, secret: token,
}, },
}); });
} catch (e) { } catch (err) {
throw new AuthenticationError("Invalid API key"); throw new AuthenticationError("Invalid API key");
} }
if (!apiKey) throw new AuthenticationError("Invalid API key"); if (!apiKey) {
throw new AuthenticationError("Invalid API key");
}
user = await User.findByPk(apiKey.userId); user = await User.findByPk(apiKey.userId);
if (!user) throw new AuthenticationError("Invalid API key"); if (!user) {
throw new AuthenticationError("Invalid API key");
}
} else { } else {
/* $FlowFixMeNowPlease This comment suppresses an error found when upgrading ctx.state.authType = "app";
* flow-bin@0.104.0. To view the error, delete this comment and run Flow. */
user = await getUserForJWT(token); user = await getUserForJWT(String(token));
} }
if (user.isSuspended) { if (user.isSuspended) {
@@ -76,21 +83,16 @@ export default function auth(options?: { required?: boolean } = {}) {
// not awaiting the promise here so that the request is not blocked // not awaiting the promise here so that the request is not blocked
user.updateActiveAt(ctx.request.ip); user.updateActiveAt(ctx.request.ip);
/* $FlowFixMeNowPlease This comment suppresses an error found when upgrading ctx.state.token = String(token);
* flow-bin@0.104.0. To view the error, delete this comment and run Flow. */
ctx.state.token = token;
/* $FlowFixMeNowPlease This comment suppresses an error found when upgrading
* flow-bin@0.104.0. To view the error, delete this comment and run Flow. */
ctx.state.user = user; ctx.state.user = user;
if (!ctx.cache) ctx.cache = {};
/* $FlowFixMeNowPlease This comment suppresses an error found when upgrading
* flow-bin@0.104.0. To view the error, delete this comment and run Flow. */
ctx.cache[user.id] = user;
} }
ctx.signIn = async (user, team, service, isFirstSignin = false) => { ctx.signIn = async (
user: User,
team: Team,
service,
isFirstSignin = false
) => {
if (user.isSuspended) { if (user.isSuspended) {
return ctx.redirect("/?notice=suspended"); return ctx.redirect("/?notice=suspended");
} }

12
server/types.js Normal file
View File

@@ -0,0 +1,12 @@
// @flow
import { type Context } from "koa";
import { User } from "./models";
export type ContextWithState = {|
...$Exact<Context>,
state: {
user: User,
token: string,
authType: "app" | "api",
},
|};

View File

@@ -18,7 +18,7 @@ function getJWTPayload(token) {
return payload; return payload;
} }
export async function getUserForJWT(token: string) { export async function getUserForJWT(token: string): Promise<User> {
const payload = getJWTPayload(token); const payload = getJWTPayload(token);
const user = await User.findByPk(payload.id); const user = await User.findByPk(payload.id);
@@ -31,7 +31,7 @@ export async function getUserForJWT(token: string) {
return user; return user;
} }
export async function getUserForEmailSigninToken(token: string) { export async function getUserForEmailSigninToken(token: string): Promise<User> {
const payload = getJWTPayload(token); const payload = getJWTPayload(token);
// check the token is within it's expiration time // check the token is within it's expiration time