fix: set cookie's `SameSite` and `Secure`

fix/auth-cookie
Arik Chakma 10 months ago
parent 7ba48523da
commit 92d562baed
  1. 11
      Caddyfile
  2. 8
      src/components/AuthenticationFlow/EmailLoginForm.tsx
  3. 8
      src/components/AuthenticationFlow/GitHubButton.tsx
  4. 8
      src/components/AuthenticationFlow/GoogleButton.tsx
  5. 8
      src/components/AuthenticationFlow/LinkedInButton.tsx
  6. 10
      src/components/AuthenticationFlow/ResetPasswordForm.tsx
  7. 8
      src/components/AuthenticationFlow/TriggerVerifyAccount.tsx
  8. 7
      src/components/Navigation/navigation.ts
  9. 4
      src/components/UpdateProfile/UploadProfilePicture.tsx
  10. 24
      src/lib/http.ts
  11. 17
      src/lib/jwt.ts

@ -0,0 +1,11 @@
api.roadmap.localhost {
reverse_proxy localhost:8080
}
roadmap.localhost {
reverse_proxy localhost:3000
}
draw.roadmap.localhost {
reverse_proxy localhost:4321
}

@ -2,7 +2,7 @@ import Cookies from 'js-cookie';
import type { FormEvent } from 'react';
import { useState } from 'react';
import { httpPost } from '../../lib/http';
import { TOKEN_COOKIE_NAME } from '../../lib/jwt';
import { TOKEN_COOKIE_NAME, setAuthToken } from '../../lib/jwt';
type EmailLoginFormProps = {
isDisabled?: boolean;
@ -34,11 +34,7 @@ export function EmailLoginForm(props: EmailLoginFormProps) {
// Log the user in and reload the page
if (response?.token) {
Cookies.set(TOKEN_COOKIE_NAME, response.token, {
path: '/',
expires: 30,
domain: import.meta.env.DEV ? 'localhost' : '.roadmap.sh',
});
setAuthToken(response.token);
window.location.reload();
return;

@ -1,7 +1,7 @@
import { useEffect, useState } from 'react';
import { GitHubIcon } from '../ReactIcons/GitHubIcon.tsx';
import Cookies from 'js-cookie';
import { TOKEN_COOKIE_NAME } from '../../lib/jwt';
import { TOKEN_COOKIE_NAME, setAuthToken } from '../../lib/jwt';
import { httpGet } from '../../lib/http';
import { Spinner } from '../ReactIcons/Spinner.tsx';
@ -70,11 +70,7 @@ export function GitHubButton(props: GitHubButtonProps) {
localStorage.removeItem(GITHUB_REDIRECT_AT);
localStorage.removeItem(GITHUB_LAST_PAGE);
Cookies.set(TOKEN_COOKIE_NAME, response.token, {
path: '/',
expires: 30,
domain: import.meta.env.DEV ? 'localhost' : '.roadmap.sh',
});
setAuthToken(response.token);
window.location.href = redirectUrl;
})
.catch((err) => {

@ -1,6 +1,6 @@
import { useEffect, useState } from 'react';
import Cookies from 'js-cookie';
import { TOKEN_COOKIE_NAME } from '../../lib/jwt';
import { TOKEN_COOKIE_NAME, setAuthToken } from '../../lib/jwt';
import { httpGet } from '../../lib/http';
import { Spinner } from '../ReactIcons/Spinner.tsx';
import { GoogleIcon } from '../ReactIcons/GoogleIcon.tsx';
@ -69,11 +69,7 @@ export function GoogleButton(props: GoogleButtonProps) {
localStorage.removeItem(GOOGLE_REDIRECT_AT);
localStorage.removeItem(GOOGLE_LAST_PAGE);
Cookies.set(TOKEN_COOKIE_NAME, response.token, {
path: '/',
expires: 30,
domain: import.meta.env.DEV ? 'localhost' : '.roadmap.sh',
});
setAuthToken(response.token);
window.location.href = redirectUrl;
})
.catch((err) => {

@ -1,6 +1,6 @@
import { useEffect, useState } from 'react';
import Cookies from 'js-cookie';
import { TOKEN_COOKIE_NAME } from '../../lib/jwt';
import { TOKEN_COOKIE_NAME, setAuthToken } from '../../lib/jwt';
import { httpGet } from '../../lib/http';
import { Spinner } from '../ReactIcons/Spinner.tsx';
import { LinkedInIcon } from '../ReactIcons/LinkedInIcon.tsx';
@ -69,11 +69,7 @@ export function LinkedInButton(props: LinkedInButtonProps) {
localStorage.removeItem(LINKEDIN_REDIRECT_AT);
localStorage.removeItem(LINKEDIN_LAST_PAGE);
Cookies.set(TOKEN_COOKIE_NAME, response.token, {
path: '/',
expires: 30,
domain: import.meta.env.DEV ? 'localhost' : '.roadmap.sh',
});
setAuthToken(response.token);
window.location.href = redirectUrl;
})
.catch((err) => {

@ -1,7 +1,7 @@
import { type FormEvent, useEffect, useState } from 'react';
import { httpPost } from '../../lib/http';
import Cookies from 'js-cookie';
import { TOKEN_COOKIE_NAME } from '../../lib/jwt';
import { TOKEN_COOKIE_NAME, setAuthToken } from '../../lib/jwt';
export function ResetPasswordForm() {
const [code, setCode] = useState('');
@ -37,7 +37,7 @@ export function ResetPasswordForm() {
newPassword: password,
confirmPassword: passwordConfirm,
code,
}
},
);
if (error?.message) {
@ -53,11 +53,7 @@ export function ResetPasswordForm() {
}
const token = response.token;
Cookies.set(TOKEN_COOKIE_NAME, token, {
path: '/',
expires: 30,
domain: import.meta.env.DEV ? 'localhost' : '.roadmap.sh',
});
setAuthToken(response.token);
window.location.href = '/';
};

@ -1,7 +1,7 @@
import { useEffect, useState } from 'react';
import Cookies from 'js-cookie';
import { httpPost } from '../../lib/http';
import { TOKEN_COOKIE_NAME } from '../../lib/jwt';
import { TOKEN_COOKIE_NAME, setAuthToken } from '../../lib/jwt';
import { Spinner } from '../ReactIcons/Spinner';
import { ErrorIcon2 } from '../ReactIcons/ErrorIcon2';
@ -26,11 +26,7 @@ export function TriggerVerifyAccount() {
return;
}
Cookies.set(TOKEN_COOKIE_NAME, response.token, {
path: '/',
expires: 30,
domain: import.meta.env.DEV ? 'localhost' : '.roadmap.sh',
});
setAuthToken(response.token);
window.location.href = '/';
})
.catch((err) => {

@ -1,11 +1,8 @@
import Cookies from 'js-cookie';
import { TOKEN_COOKIE_NAME } from '../../lib/jwt';
import { TOKEN_COOKIE_NAME, removeAuthToken } from '../../lib/jwt';
export function logout() {
Cookies.remove(TOKEN_COOKIE_NAME, {
path: '/',
domain: import.meta.env.DEV ? 'localhost' : '.roadmap.sh',
});
removeAuthToken();
// Reloading will automatically redirect the user if required
window.location.reload();

@ -1,6 +1,6 @@
import Cookies from 'js-cookie';
import { type ChangeEvent, type FormEvent, useEffect, useRef, useState } from 'react';
import { TOKEN_COOKIE_NAME } from '../../lib/jwt';
import { TOKEN_COOKIE_NAME, removeAuthToken } from '../../lib/jwt';
interface PreviewFile extends File {
preview: string;
@ -128,7 +128,7 @@ export default function UploadProfilePicture(props: UploadProfilePictureProps) {
// Logout user if token is invalid
if (data.status === 401) {
Cookies.remove(TOKEN_COOKIE_NAME);
removeAuthToken();
window.location.reload();
}
};

@ -1,6 +1,6 @@
import Cookies from 'js-cookie';
import fp from '@fingerprintjs/fingerprintjs';
import { TOKEN_COOKIE_NAME } from './jwt';
import { TOKEN_COOKIE_NAME, removeAuthToken } from './jwt';
type HttpOptionsType = RequestInit | { headers: Record<string, any> };
@ -30,10 +30,10 @@ type ApiReturn<ResponseType, ErrorType> = {
*/
export async function httpCall<
ResponseType = AppResponse,
ErrorType = AppError
ErrorType = AppError,
>(
url: string,
options?: HttpOptionsType
options?: HttpOptionsType,
): Promise<ApiReturn<ResponseType, ErrorType>> {
try {
const fingerprintPromise = await fp.load({ monitoring: false });
@ -65,7 +65,7 @@ export async function httpCall<
// Logout user if token is invalid
if (data.status === 401) {
Cookies.remove(TOKEN_COOKIE_NAME);
removeAuthToken();
window.location.reload();
return { response: undefined, error: data as ErrorType };
}
@ -92,11 +92,11 @@ export async function httpCall<
export async function httpPost<
ResponseType = AppResponse,
ErrorType = AppError
ErrorType = AppError,
>(
url: string,
body: Record<string, any>,
options?: HttpOptionsType
options?: HttpOptionsType,
): Promise<ApiReturn<ResponseType, ErrorType>> {
return httpCall<ResponseType, ErrorType>(url, {
...options,
@ -108,7 +108,7 @@ export async function httpPost<
export async function httpGet<ResponseType = AppResponse, ErrorType = AppError>(
url: string,
queryParams?: Record<string, any>,
options?: HttpOptionsType
options?: HttpOptionsType,
): Promise<ApiReturn<ResponseType, ErrorType>> {
const searchParams = new URLSearchParams(queryParams).toString();
const queryUrl = searchParams ? `${url}?${searchParams}` : url;
@ -122,11 +122,11 @@ export async function httpGet<ResponseType = AppResponse, ErrorType = AppError>(
export async function httpPatch<
ResponseType = AppResponse,
ErrorType = AppError
ErrorType = AppError,
>(
url: string,
body: Record<string, any>,
options?: HttpOptionsType
options?: HttpOptionsType,
): Promise<ApiReturn<ResponseType, ErrorType>> {
return httpCall<ResponseType, ErrorType>(url, {
...options,
@ -138,7 +138,7 @@ export async function httpPatch<
export async function httpPut<ResponseType = AppResponse, ErrorType = AppError>(
url: string,
body: Record<string, any>,
options?: HttpOptionsType
options?: HttpOptionsType,
): Promise<ApiReturn<ResponseType, ErrorType>> {
return httpCall<ResponseType, ErrorType>(url, {
...options,
@ -149,10 +149,10 @@ export async function httpPut<ResponseType = AppResponse, ErrorType = AppError>(
export async function httpDelete<
ResponseType = AppResponse,
ErrorType = AppError
ErrorType = AppError,
>(
url: string,
options?: HttpOptionsType
options?: HttpOptionsType,
): Promise<ApiReturn<ResponseType, ErrorType>> {
return httpCall<ResponseType, ErrorType>(url, {
...options,

@ -31,3 +31,20 @@ export function getUser() {
return decodeToken(token);
}
export function setAuthToken(token: string) {
Cookies.set(TOKEN_COOKIE_NAME, token, {
path: '/',
expires: 30,
sameSite: 'lax',
secure: true,
domain: import.meta.env.DEV ? 'localhost' : '.roadmap.sh',
});
}
export function removeAuthToken() {
Cookies.remove(TOKEN_COOKIE_NAME, {
path: '/',
domain: import.meta.env.DEV ? 'localhost' : '.roadmap.sh',
});
}

Loading…
Cancel
Save