mirror of
https://github.com/kremalicious/umami.git
synced 2025-02-14 21:10:34 +01:00
Switch to authentication using bearer token.
This commit is contained in:
parent
698d4d2687
commit
d8e831db50
@ -13,6 +13,8 @@ import Icon from 'components/common/Icon';
|
|||||||
import Logo from 'assets/logo.svg';
|
import Logo from 'assets/logo.svg';
|
||||||
import styles from './LoginForm.module.css';
|
import styles from './LoginForm.module.css';
|
||||||
import usePost from 'hooks/usePost';
|
import usePost from 'hooks/usePost';
|
||||||
|
import { setItem } from 'lib/web';
|
||||||
|
import { AUTH_TOKEN } from '../../lib/constants';
|
||||||
|
|
||||||
const validate = ({ username, password }) => {
|
const validate = ({ username, password }) => {
|
||||||
const errors = {};
|
const errors = {};
|
||||||
@ -39,6 +41,8 @@ export default function LoginForm() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (ok) {
|
if (ok) {
|
||||||
|
setItem(AUTH_TOKEN, data.token);
|
||||||
|
|
||||||
return router.push('/');
|
return router.push('/');
|
||||||
} else {
|
} else {
|
||||||
setMessage(
|
setMessage(
|
||||||
|
@ -7,6 +7,8 @@ import Icon from 'components/common/Icon';
|
|||||||
import User from 'assets/user.svg';
|
import User from 'assets/user.svg';
|
||||||
import Chevron from 'assets/chevron-down.svg';
|
import Chevron from 'assets/chevron-down.svg';
|
||||||
import styles from './UserButton.module.css';
|
import styles from './UserButton.module.css';
|
||||||
|
import { removeItem } from 'lib/web';
|
||||||
|
import { AUTH_TOKEN } from 'lib/constants';
|
||||||
|
|
||||||
export default function UserButton() {
|
export default function UserButton() {
|
||||||
const user = useSelector(state => state.user);
|
const user = useSelector(state => state.user);
|
||||||
@ -30,7 +32,8 @@ export default function UserButton() {
|
|||||||
|
|
||||||
function handleSelect(value) {
|
function handleSelect(value) {
|
||||||
if (value === 'logout') {
|
if (value === 'logout') {
|
||||||
router.push('/logout');
|
removeItem(AUTH_TOKEN);
|
||||||
|
router.push('/login');
|
||||||
} else if (value === 'profile') {
|
} else if (value === 'profile') {
|
||||||
router.push('/settings/profile');
|
router.push('/settings/profile');
|
||||||
}
|
}
|
||||||
|
@ -11,13 +11,14 @@ export default function useFetch(url, options = {}, update = []) {
|
|||||||
const [loading, setLoadiing] = useState(false);
|
const [loading, setLoadiing] = useState(false);
|
||||||
const [count, setCount] = useState(0);
|
const [count, setCount] = useState(0);
|
||||||
const { basePath } = useRouter();
|
const { basePath } = useRouter();
|
||||||
const { params = {}, disabled, headers, delay = 0, interval, onDataLoad } = options;
|
const { params = {}, headers = {}, disabled, delay = 0, interval, onDataLoad } = options;
|
||||||
|
|
||||||
async function loadData(params) {
|
async function loadData(params) {
|
||||||
try {
|
try {
|
||||||
setLoadiing(true);
|
setLoadiing(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
const time = performance.now();
|
const time = performance.now();
|
||||||
|
|
||||||
const { data, status, ok } = await get(`${basePath}${url}`, params, headers);
|
const { data, status, ok } = await get(`${basePath}${url}`, params, headers);
|
||||||
|
|
||||||
dispatch(updateQuery({ url, time: performance.now() - time, completed: Date.now() }));
|
dispatch(updateQuery({ url, time: performance.now() - time, completed: Date.now() }));
|
||||||
|
11
lib/auth.js
11
lib/auth.js
@ -1,12 +1,15 @@
|
|||||||
import { parse } from 'cookie';
|
|
||||||
import { parseSecureToken, parseToken } from './crypto';
|
import { parseSecureToken, parseToken } from './crypto';
|
||||||
import { AUTH_COOKIE_NAME, TOKEN_HEADER } from './constants';
|
import { TOKEN_HEADER } from './constants';
|
||||||
import { getWebsiteById } from './queries';
|
import { getWebsiteById } from './queries';
|
||||||
|
|
||||||
export async function getAuthToken(req) {
|
export async function getAuthToken(req) {
|
||||||
const token = parse(req.headers.cookie || '')[AUTH_COOKIE_NAME];
|
try {
|
||||||
|
const token = req.headers.authorization;
|
||||||
|
|
||||||
return parseSecureToken(token);
|
return parseSecureToken(token.split(' ')[1]);
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function isValidToken(token, validation) {
|
export async function isValidToken(token, validation) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
export const AUTH_COOKIE_NAME = 'umami.auth';
|
export const AUTH_TOKEN = 'umami.auth';
|
||||||
export const LOCALE_CONFIG = 'umami.locale';
|
export const LOCALE_CONFIG = 'umami.locale';
|
||||||
export const TIMEZONE_CONFIG = 'umami.timezone';
|
export const TIMEZONE_CONFIG = 'umami.timezone';
|
||||||
export const DATE_RANGE_CONFIG = 'umami.date-range';
|
export const DATE_RANGE_CONFIG = 'umami.date-range';
|
||||||
@ -80,7 +80,8 @@ export const POSTGRESQL_DATE_FORMATS = {
|
|||||||
year: 'YYYY-01-01',
|
year: 'YYYY-01-01',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DOMAIN_REGEX = /^(localhost(:[1-9]\d{0,4})?|((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63})$/;
|
export const DOMAIN_REGEX =
|
||||||
|
/^(localhost(:[1-9]\d{0,4})?|((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63})$/;
|
||||||
|
|
||||||
export const DESKTOP_SCREEN_WIDTH = 1920;
|
export const DESKTOP_SCREEN_WIDTH = 1920;
|
||||||
export const LAPTOP_SCREEN_WIDTH = 1024;
|
export const LAPTOP_SCREEN_WIDTH = 1024;
|
||||||
|
15
lib/web.js
15
lib/web.js
@ -1,13 +1,17 @@
|
|||||||
import { makeUrl } from './url';
|
import { makeUrl } from './url';
|
||||||
|
import { AUTH_TOKEN } from './constants';
|
||||||
|
|
||||||
export const apiRequest = (method, url, body, headers) =>
|
export const apiRequest = (method, url, body, headers) => {
|
||||||
fetch(url, {
|
const authToken = getItem(AUTH_TOKEN);
|
||||||
|
|
||||||
|
return fetch(url, {
|
||||||
method,
|
method,
|
||||||
cache: 'no-cache',
|
cache: 'no-cache',
|
||||||
credentials: 'same-origin',
|
credentials: 'same-origin',
|
||||||
headers: {
|
headers: {
|
||||||
Accept: 'application/json',
|
Accept: 'application/json',
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
|
...(authToken ? { Authorization: `Bearer ${authToken}` } : {}),
|
||||||
...headers,
|
...headers,
|
||||||
},
|
},
|
||||||
body,
|
body,
|
||||||
@ -18,6 +22,7 @@ export const apiRequest = (method, url, body, headers) =>
|
|||||||
|
|
||||||
return res.text().then(data => ({ ok: res.ok, status: res.status, res: res, data }));
|
return res.text().then(data => ({ ok: res.ok, status: res.status, res: res, data }));
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const get = (url, params, headers) =>
|
export const get = (url, params, headers) =>
|
||||||
apiRequest('get', makeUrl(url, params), undefined, headers);
|
apiRequest('get', makeUrl(url, params), undefined, headers);
|
||||||
@ -64,3 +69,9 @@ export const getItem = (key, session) =>
|
|||||||
typeof window !== 'undefined'
|
typeof window !== 'undefined'
|
||||||
? JSON.parse((session ? sessionStorage : localStorage).getItem(key))
|
? JSON.parse((session ? sessionStorage : localStorage).getItem(key))
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
|
export const removeItem = (key, session) => {
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
(session ? sessionStorage : localStorage).removeItem(key);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import { serialize } from 'cookie';
|
|
||||||
import { checkPassword, createSecureToken } from 'lib/crypto';
|
import { checkPassword, createSecureToken } from 'lib/crypto';
|
||||||
import { getAccountByUsername } from 'lib/queries';
|
import { getAccountByUsername } from 'lib/queries';
|
||||||
import { AUTH_COOKIE_NAME } from 'lib/constants';
|
|
||||||
import { ok, unauthorized, badRequest } from 'lib/response';
|
import { ok, unauthorized, badRequest } from 'lib/response';
|
||||||
|
|
||||||
export default async (req, res) => {
|
export default async (req, res) => {
|
||||||
@ -16,14 +14,6 @@ export default async (req, res) => {
|
|||||||
if (account && (await checkPassword(password, account.password))) {
|
if (account && (await checkPassword(password, account.password))) {
|
||||||
const { user_id, username, is_admin } = account;
|
const { user_id, username, is_admin } = account;
|
||||||
const token = await createSecureToken({ user_id, username, is_admin });
|
const token = await createSecureToken({ user_id, username, is_admin });
|
||||||
const cookie = serialize(AUTH_COOKIE_NAME, token, {
|
|
||||||
path: '/',
|
|
||||||
httpOnly: true,
|
|
||||||
sameSite: true,
|
|
||||||
maxAge: 60 * 60 * 24 * 365,
|
|
||||||
});
|
|
||||||
|
|
||||||
res.setHeader('Set-Cookie', [cookie]);
|
|
||||||
|
|
||||||
return ok(res, { token });
|
return ok(res, { token });
|
||||||
}
|
}
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
import { serialize } from 'cookie';
|
|
||||||
import { AUTH_COOKIE_NAME } from 'lib/constants';
|
|
||||||
import { ok } from 'lib/response';
|
|
||||||
|
|
||||||
export default async (req, res) => {
|
|
||||||
const cookie = serialize(AUTH_COOKIE_NAME, '', {
|
|
||||||
path: '/',
|
|
||||||
httpOnly: true,
|
|
||||||
maxAge: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
res.setHeader('Set-Cookie', [cookie]);
|
|
||||||
|
|
||||||
return ok(res);
|
|
||||||
};
|
|
Loading…
x
Reference in New Issue
Block a user