A Client-Side JS Authentication and Authorization Library
TypeScriptMIT
skeleton-key
A Client-Side JS Authentication and Authorization Library for Commons Auth
Install
npm i @rocketbase/skeleton-key
Examples
// auth.tsimport{SkeletonKey}from"@rocketbase/skeleton-key";exportconstauth=newSkeletonKey();// api.tsimport{auth}from"./auth";importaxiosfrom"axios";exportasyncfunctiongetData(){if(!auth.isLoggedIn())thrownewError("Need to be logged in!");// Auth headers are appended automatically.returnaxios.get('/the/data');}exportasyncfunctiongetOtherData(){// Auth headers are not appended unless this app is hosted on example.comreturnaxios.get('https://example.com/other/data');}exportfunctiongetUserId(){// Data encoded in the jwt is accessible through tokenData and refreshTokenData getter.returnauth.tokenData.payload.customerId;}exportfunctiongetUserAttributes(){// Data returned as the user response is accessible through the userData getter.returnauth.userData;}
// auth.tsimport{SkeletonKey}from"@rocketbase/skeleton-key";interfaceUserExtensions{customAttributes: string;goHere: boolean;forTypingAssist: any[]}interfaceJwtExtensions{tokenExtensions: string;goHere: number;forTypingAssist: any}exportconstauth=newSkeletonKey<UserExtensions,JwtExtensions>({domains: ['*'],// Will insert auth headers for every domainurl: "https://the.auth.server/path",// Path to the auth server to run requests againstintercept: true,// Automatically intercept all requests and insert headers where necessaryrenewType: "action",// Automatically refresh token once it expiresauthHeader: "Authorization",// The header to inject the token underauthPrefix: "Bearer ",// Prefix of the header valueauthSuffix: "",// Suffix of the header valuestorageKey: "io.rocketbase.commons.auth"// The key in localStorage the token and user data is persisted under});// decorators.tsimport{auth}from"./auth";import{LoginDialog}from"./some/LoginDialog";/** * Checks if a user is logged in. * If not, delays execution until successful login and trigger a login dialog */exportfunctionNeedsLogin(target: any,propertyKey: string|symbol,desc: PropertyDescriptor){const{value}=desc;desc.value=asyncfunction(this: any, ...args: any[]){if(!auth.isLoggedIn()){LoginDialog.open();awaitauth.waitForLogin();}returnvalue.apply(this,args);};returndesc;}/** * Checks if a user has a given role. * If not, prevents execution and throws an error. */exportfunctionNeedsRole(role: string){returnfunction(target: any,propertyKey: string|symbol,desc: PropertyDescriptor){const{value}=desc;desc.value=function(this: any, ...args: any[]){if(!auth.isLoggedIn()||!auth.userData.roles.contains(role))thrownewError(`User needs to be member of role ${role}!`);returnvalue.apply(this,args);}returndesc;}}// api.tsimport{NeedsLogin,NeedsRole}from"./decorators";importaxiosfrom"axios";exportclassApi{
@NeedsLoginasyncgetSomeData(id: string){returnaxios.get(`/path/to/service/data/${id}`);}
@NeedsRole("ADMIN")asyncgetUserData(id: string){returnaxios.get(`/path/to/service/users/${id}`);}// Can also be combined
@NeedsLogin
@NeedsRole("ADMIN")asyncsetUserPassword(id: string,password: string){returnaxios.put(`/path/to/service/users/${id}`,JSON.stringify({password}),{headers:{'Content-Type': 'application/json'}});}}