feat: improve authentication checks and request lib

This commit is contained in:
janishutz committed 2026-09-21 15:51:23 +02:00
1 parent 374e1dfc9e
commit e3d1bd7a62
3 files changed
+66 -15

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@janishutz/oidc-login-sdk-browser", "name": "@janishutz/oidc-login-sdk-browser",
"version": "1.1.0", "version": "1.2.0",
"description": "SDK to communicate with and log into a backend running express-openid-connect", "description": "SDK to communicate with and log into a backend running express-openid-connect",
"homepage": "https://github.com/janishutz/oidc-login-sdk#readme", "homepage": "https://github.com/janishutz/oidc-login-sdk#readme",
"bugs": { "bugs": {
+17 -3
View File
@@ -3,12 +3,20 @@ import request, {
} from './request.js'; } from './request.js';
import config from './config.js'; import config from './config.js';
export const login = ( returnTo?: string ) => { /**
* Start the login flow. Please note that upon calling this, the page will be reloaded after completion of the login flow
* @param returnTo - The location to return to after login
*/
export const login = ( returnTo?: URL ) => {
sessionStorage.setItem( 'redirect', location.pathname ); sessionStorage.setItem( 'redirect', location.pathname );
location.href = ( config.get().loginEndpoint ?? '/auth/v2/login' ) + ( returnTo ? returnTo : '' ); location.href = ( config.get().loginEndpoint ?? '/auth/v2/login' ) + ( returnTo ? returnTo.toString() : '' );
}; };
export const check = async () => { /**
* Check if a user is authenticated. This can also be done implicitly using a call to a protected endpoint
* @returns A promise resolving to a boolean indicating authentication status
*/
export const check = async (): Promise<boolean> => {
let status: boolean; let status: boolean;
try { try {
@@ -31,6 +39,11 @@ export const check = async () => {
return status; return status;
}; };
/**
* Use this, if you did not set the login returnTo path, or if in any other case you need to redirect the user after login,
* such as after a navigation guard executing prior to state update
* @returns The location to redirect to
*/
export const getRedirect = (): string | null => { export const getRedirect = (): string | null => {
const item = sessionStorage.getItem( 'redirect' ); const item = sessionStorage.getItem( 'redirect' );
@@ -39,6 +52,7 @@ export const getRedirect = (): string | null => {
return item; return item;
}; };
/** Logs the user out */
export const logout = async () => { export const logout = async () => {
location.href = config.get().logoutEndpoint ?? '/auth/v2/logout'; location.href = config.get().logoutEndpoint ?? '/auth/v2/logout';
}; };
+48 -11
View File
@@ -11,12 +11,26 @@ export class AuthError extends Error {}
export class UnownedError extends Error {} export class UnownedError extends Error {}
/**
* Perform an HTTP GET request
* @param url - The URL (just the path relative to website root) to fetch
* @param authErrorResolution - The mean of authentication error resolution
* @returns The reponse
*/
const get = async ( url: string, authErrorResolution?: AuthErrorResolution ): Promise<Response> => { const get = async ( url: string, authErrorResolution?: AuthErrorResolution ): Promise<Response> => {
return await wrapper( url, { return await wrapper( url, {
'credentials': 'include' 'credentials': 'include'
}, authErrorResolution ); }, authErrorResolution );
}; };
/**
* Perform an HTTP POST request
* @param url - The URL (just the path relative to website root) to fetch
* @param payload - The request body to send as a string
* @param mime - The MIME type of the payload
* @param authErrorResolution - The mean of authentication error resolution
* @returns The response
*/
const post = async ( url: string, payload: string, mime: string = 'application/json', authErrorResolution?: AuthErrorResolution ): Promise<Response> => { const post = async ( url: string, payload: string, mime: string = 'application/json', authErrorResolution?: AuthErrorResolution ): Promise<Response> => {
return await wrapper( url, { return await wrapper( url, {
'credentials': 'include', 'credentials': 'include',
@@ -28,6 +42,19 @@ const post = async ( url: string, payload: string, mime: string = 'application/j
}, authErrorResolution ); }, authErrorResolution );
}; };
/**
* Perform an HTTP DELETE request
* @param url - The URL (just the path relative to website root) to fetch
* @param authErrorResolution - The mean of authentication error resolution
* @returns The response
*/
const deleteRequest = async ( url: string, authErrorResolution?: AuthErrorResolution ): Promise<Response> => {
return await wrapper( url, {
'credentials': 'include',
'method': 'delete'
}, authErrorResolution );
};
const wrapper = async ( url: string, opts: RequestInit, authErrorResolution?: AuthErrorResolution ): Promise<Response> => { const wrapper = async ( url: string, opts: RequestInit, authErrorResolution?: AuthErrorResolution ): Promise<Response> => {
const res = await fetch( config.get().backendURL + url, { const res = await fetch( config.get().backendURL + url, {
'redirect': 'manual', 'redirect': 'manual',
@@ -35,23 +62,33 @@ const wrapper = async ( url: string, opts: RequestInit, authErrorResolution?: Au
} ); } );
if ( res.type === 'opaqueredirect' ) { if ( res.type === 'opaqueredirect' ) {
if ( config.get().authErrorEvent ) { handleUnauth( authErrorResolution );
document.dispatchEvent( new CustomEvent( 'autherror' ) );
}
if ( ( authErrorResolution && authErrorResolution === 'resolve' ) || ( !authErrorResolution && config.get().defaultAuthErrorResolution === 'resolve' ) ) {
login();
} else {
throw new AuthError( 'ERR_USER_UNAUTHORIZED' );
}
return res; return res;
} else { } else {
return res; if ( res.ok )
return res;
else if ( res.status === 401 || res.status === 403 )
throw new AuthError( 'ERR_USER_UNAUTHORIZED' );
else
throw new Error( 'ERR_' + res.status );
}
};
const handleUnauth = ( authErrorResolution: AuthErrorResolution ) => {
if ( config.get().authErrorEvent ) {
document.dispatchEvent( new CustomEvent( 'autherror' ) );
}
if ( ( authErrorResolution && authErrorResolution === 'resolve' ) || ( !authErrorResolution && config.get().defaultAuthErrorResolution === 'resolve' ) ) {
login();
} else {
throw new AuthError( 'ERR_USER_UNAUTHORIZED' );
} }
}; };
export default { export default {
get, get,
post post,
'delete': deleteRequest
}; };