Skip to content

Commit

Permalink
fix: add accessToken to cookie
Browse files Browse the repository at this point in the history
  • Loading branch information
solufa committed Sep 17, 2024
1 parent 48faf74 commit ceb7fe1
Show file tree
Hide file tree
Showing 9 changed files with 47 additions and 24 deletions.
8 changes: 5 additions & 3 deletions client/features/auth/AuthLoader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ export const AuthLoader = () => {
const { setLoading } = useLoading();
const { setAlert } = useAlert();
const updateCookie = useCallback(async () => {
const jwt = await fetchAuthSession().then((e) => e.tokens?.idToken?.toString());
const tokens = await fetchAuthSession().then((e) => e.tokens);

if (jwt !== undefined) {
await apiClient.session.$post({ body: { jwt } });
if (tokens !== undefined && tokens.idToken !== undefined) {
await apiClient.session.$post({
body: { idToken: tokens.idToken.toString(), accessToken: tokens.accessToken.toString() },
});
await apiClient.private.me.$get().then(setUser);
} else {
setUser(null);
Expand Down
8 changes: 6 additions & 2 deletions client/public/docs/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -692,12 +692,16 @@
"schema": {
"type": "object",
"properties": {
"jwt": {
"idToken": {
"type": "string"
},
"accessToken": {
"type": "string"
}
},
"required": [
"jwt"
"accessToken",
"idToken"
]
}
}
Expand Down
4 changes: 2 additions & 2 deletions server/api/private/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import assert from 'assert';
import type { UserDto } from 'common/types/user';
import { userUseCase } from 'domain/user/useCase/userUseCase';
import { COOKIE_NAME } from 'service/constants';
import { COOKIE_NAMES } from 'service/constants';
import type { JwtUser } from 'service/types';
import { defineHooks } from './$relay';

Expand All @@ -12,7 +12,7 @@ export default defineHooks(() => ({
req.user = await req
.jwtVerify<JwtUser>({ onlyCookie: true })
.then((jwtUser) => {
const token = req.cookies[COOKIE_NAME];
const token = req.cookies[COOKIE_NAMES.accessToken];
assert(token);

return userUseCase.findOrCreateUser(jwtUser, token);
Expand Down
16 changes: 11 additions & 5 deletions server/api/session/controller.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { CookieSerializeOptions } from '@fastify/cookie';
import assert from 'assert';
import { COOKIE_NAME } from 'service/constants';
import { COOKIE_NAMES } from 'service/constants';
import { z } from 'zod';
import type { Methods } from '.';
import { defineController } from './$relay';
Expand All @@ -18,17 +18,22 @@ const options: CookieSerializeOptions = {

export default defineController((fastify) => ({
post: {
validators: { body: z.object({ jwt: z.string() }) },
validators: { body: z.object({ idToken: z.string(), accessToken: z.string() }) },
hooks: {
preHandler: (req, reply, done) => {
assert(req.body);

const decoded = z
.object({ payload: z.object({ exp: z.number() }).passthrough() })
.passthrough()
.parse(fastify.jwt.decode(req.body.jwt));
.parse(fastify.jwt.decode(req.body.idToken));

reply.setCookie(COOKIE_NAME, req.body.jwt, {
reply.setCookie(COOKIE_NAMES.idToken, req.body.idToken, {
...options,
expires: new Date(decoded.payload.exp * 1000),
});

reply.setCookie(COOKIE_NAMES.accessToken, req.body.accessToken, {
...options,
expires: new Date(decoded.payload.exp * 1000),
});
Expand All @@ -41,7 +46,8 @@ export default defineController((fastify) => ({
delete: {
hooks: {
preHandler: (_, reply, done) => {
reply.clearCookie(COOKIE_NAME, options);
reply.clearCookie(COOKIE_NAMES.idToken, options);
reply.clearCookie(COOKIE_NAMES.accessToken, options);
done();
},
},
Expand Down
2 changes: 1 addition & 1 deletion server/api/session/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { DefineMethods } from 'aspida';

export type Methods = DefineMethods<{
post: {
reqBody: { jwt: string };
reqBody: { idToken: string; accessToken: string };
resBody: { status: 'success' };
};
delete: {
Expand Down
4 changes: 2 additions & 2 deletions server/service/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type { FastifyInstance, FastifyRequest } from 'fastify';
import Fastify from 'fastify';
import buildGetJwks from 'get-jwks';
import server from '../$server';
import { COOKIE_NAME } from './constants';
import { COOKIE_NAMES } from './constants';
import { CustomError } from './customAssert';
import {
API_BASE_PATH,
Expand All @@ -29,7 +29,7 @@ export const init = (): FastifyInstance => {
fastify.register(cookie);

fastify.register(fastifyJwt, {
cookie: { cookieName: COOKIE_NAME, signed: false },
cookie: { cookieName: COOKIE_NAMES.idToken, signed: false },
decode: { complete: true },
secret: (_: FastifyRequest, token: TokenOrHeader) => {
assert('header' in token);
Expand Down
2 changes: 1 addition & 1 deletion server/service/constants.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export const COOKIE_NAME = 'session';
export const COOKIE_NAMES = { idToken: 'idToken', accessToken: 'accessToken' };
7 changes: 5 additions & 2 deletions server/tests/api/apiClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
} from '@aws-sdk/client-cognito-identity-provider';
import api from 'api/$api';
import axios from 'axios';
import { COOKIE_NAME } from 'service/constants';
import { COOKIE_NAMES } from 'service/constants';
import {
API_BASE_PATH,
COGNITO_USER_POOL_CLIENT_ID,
Expand Down Expand Up @@ -47,7 +47,10 @@ export const createSessionClients = async (option?: {

const cookie = await cognitoClient
.send(command2)
.then((res) => `${COOKIE_NAME}=${res.AuthenticationResult?.IdToken}`);
.then(
(res) =>
`${COOKIE_NAMES.idToken}=${res.AuthenticationResult?.IdToken};${COOKIE_NAMES.accessToken}=${res.AuthenticationResult?.AccessToken}`,
);

const agent = axios.create({ baseURL, headers: { cookie, 'Content-Type': 'text/plain' } });

Expand Down
20 changes: 14 additions & 6 deletions server/tests/api/public.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createSigner } from 'fast-jwt';
import { COOKIE_NAME } from 'service/constants';
import { COOKIE_NAMES } from 'service/constants';
import { ulid } from 'ulid';
import { expect, test } from 'vitest';
import { createSessionClients, noCookieClient } from './apiClient';
import { DELETE, GET, POST } from './utils';
Expand All @@ -21,17 +22,24 @@ test(GET(noCookieClient.health), async () => {
});

test(POST(noCookieClient.session), async () => {
const jwt = createSigner({ key: 'dummy' })({ exp: Math.floor(Date.now() / 1000) + 100 });
const res = await noCookieClient.session.post({ body: { jwt } });

expect(res.headers['set-cookie'][0].startsWith(`${COOKIE_NAME}=${jwt};`)).toBeTruthy();
const idToken = createSigner({ key: 'dummy' })({ exp: Math.floor(Date.now() / 1000) + 100 });
const accessToken = ulid();
const res = await noCookieClient.session.post({ body: { idToken, accessToken } });

expect(
res.headers['set-cookie'][0].startsWith(`${COOKIE_NAMES.idToken}=${idToken};`),
).toBeTruthy();
expect(
res.headers['set-cookie'][1].startsWith(`${COOKIE_NAMES.accessToken}=${accessToken};`),
).toBeTruthy();
expect(res.body.status === 'success').toBeTruthy();
});

test(DELETE(noCookieClient.session), async () => {
const apiClient = await createSessionClients();
const res = await apiClient.session.delete();

expect(res.headers['set-cookie'][0].startsWith(`${COOKIE_NAME}=;`)).toBeTruthy();
expect(res.headers['set-cookie'][0].startsWith(`${COOKIE_NAMES.idToken}=;`)).toBeTruthy();
expect(res.headers['set-cookie'][1].startsWith(`${COOKIE_NAMES.accessToken}=;`)).toBeTruthy();
expect(res.body.status === 'success').toBeTruthy();
});

0 comments on commit ceb7fe1

Please sign in to comment.