sk-oidc-oauth
Version:
OIDC/OAuth2 authentication and authorization for prerendered/client-side-rendered SvelteKit apps.
204 lines (203 loc) • 7.56 kB
JavaScript
import { derived, writable } from 'svelte/store';
import { Log, Logger, UserManager } from 'oidc-client-ts-lumeris';
export class AuthService {
static mgr;
static _isLoading = writable(true);
static _isAuthenticated = writable(false);
static _accessToken = writable('');
static _idToken = writable('');
static _userInfo = writable({});
static _authError = writable(null);
static _isExpired = derived(this._userInfo, ($userInfo) => $userInfo?.profile?.exp ? new Date($userInfo.profile.exp * 1000) < new Date() : true);
static _isFullyAuthenticated = derived([this._isLoading, this._authError, this._isAuthenticated, this._isExpired], ([$isLoading, $authError, $isAuthenticated, $isExpired]) => !$isLoading && !$authError && $isAuthenticated && !$isExpired);
constructor(settings, userManager = UserManager, logger = console, logLevel = Log.ERROR) {
Log.setLogger(logger);
Log.setLevel(logLevel);
AuthService.mgr = userManager ? new userManager(settings) : new UserManager(settings);
AuthService.mgr.events.addAccessTokenExpiring(() => {
Logger.info('AuthService', 'token expiring');
AuthService.mgr
.signinSilent()
.then(function (user) {
Logger.info('AuthService', 'silent renew success', user);
})
.catch(function (e) {
Logger.error('AuthService', 'silent renew error', e.message);
});
});
AuthService.mgr.events.addAccessTokenExpired(() => {
Logger.info('AuthService', 'token expired');
});
AuthService.mgr.events.addSilentRenewError((e) => {
Logger.error('AuthService', 'silent renew error', e.message);
});
AuthService.mgr.events.addUserLoaded((user) => {
AuthService.isAuthenticated.set(true);
AuthService.accessToken.set(user.access_token);
AuthService.idToken.set(user?.id_token ?? '');
AuthService.userInfo.set({ ...user });
Logger.info('AuthService', 'user loaded', user);
});
AuthService.mgr.events.addUserUnloaded(() => {
AuthService.isAuthenticated.set(false);
AuthService.accessToken.set('');
AuthService.idToken.set('');
AuthService.userInfo.set({});
Logger.info('AuthService', 'user unloaded');
});
AuthService.mgr.events.addUserSignedIn(() => {
Logger.info('AuthService', 'user logged in to the token server');
});
AuthService.mgr.events.addUserSignedOut(() => {
Logger.info('AuthService', 'user logged out of the token server');
});
}
static get isLoading() {
return AuthService._isLoading;
}
static get isAuthenticated() {
return AuthService._isAuthenticated;
}
static get isFullyAuthenticated() {
return AuthService._isFullyAuthenticated;
}
static get isExpired() {
return AuthService._isExpired;
}
static get accessToken() {
return AuthService._accessToken;
}
static get idToken() {
return AuthService._idToken;
}
static get userInfo() {
return AuthService._userInfo;
}
static get authError() {
return AuthService._authError;
}
static async clearLocalStaleState() {
try {
await AuthService.mgr.clearStaleState();
Logger.info('AuthService', 'clearState success');
}
catch (err) {
Logger.error('AuthService', 'clearState error', err?.message);
}
}
static async getUser() {
try {
const user = await AuthService.mgr.getUser();
Logger.info('AuthService', 'got user', user);
return user;
}
catch (err) {
Logger.error('AuthService', 'getUser error', err?.message);
return null;
}
}
static async removeLocalUser() {
try {
await AuthService.mgr.removeUser();
Logger.info('AuthService', 'user removed');
}
catch (err) {
Logger.error('AuthService', 'removeUser error', err?.message);
}
}
static async querySessionStatus() {
try {
const status = await AuthService.mgr.querySessionStatus();
Logger.info('AuthService', "user's session status", status);
}
catch (err) {
Logger.error('AuthService', 'querySessionStatus error', err?.message);
}
}
static async revokeAccessToken() {
try {
await AuthService.mgr.revokeTokens();
Logger.info('AuthService', 'access token revoked');
}
catch (err) {
Logger.info('AuthService', 'revokeAccessToken error', err.message);
}
}
static async startSigninRedirect(redirectTo, additionalState) {
try {
const addState = additionalState ?? {};
await AuthService.mgr.signinRedirect({
state: {
pathname: redirectTo ?? window.location.pathname,
search: window.location.search,
...addState,
},
});
Logger.info('AuthService', 'signinRedirect done');
}
catch (err) {
Logger.error('AuthService', 'startSigninMainWindow error', err?.message);
}
}
static async endSigninRedirect() {
try {
const user = await AuthService.mgr.signinCallback();
// custom state after the login in user.state
Logger.info('AuthService', "here's our post-login custom state", user);
return user;
}
catch (err) {
Logger.error('AuthService', 'endSigninMainWindow error', err?.message);
}
}
static async popupSignin() {
try {
const user = await AuthService.mgr.signinPopup();
Logger.info('AuthService', 'signed in', user);
}
catch (err) {
Logger.error('AuthService', 'popupSignin error', err?.message);
}
}
static async popupSignout() {
try {
await AuthService.mgr.signoutPopup();
Logger.info('AuthService', 'signed out');
}
catch (err) {
Logger.error('AuthService', 'popupSignout error', err?.message);
}
}
static async silentSignin() {
try {
const user = await AuthService.mgr.signinSilent();
Logger.info('AuthService', 'signed in silent', user);
}
catch (err) {
Logger.error('AuthService', 'iframeSignin error', err?.message);
}
}
static async startSignoutRedirect() {
try {
const resp = await AuthService.mgr.signoutRedirect({
state: {
pathname: window.location.pathname,
search: window.location.search,
},
});
Logger.info('AuthService', 'signed out', resp);
}
catch (err) {
Logger.error('AuthService', 'startSignoutMainWindow error', err?.message);
}
}
static async endSignoutRedirect() {
try {
const resp = await AuthService.mgr.signoutCallback();
Logger.info('AuthService', 'signed out', resp);
}
catch (err) {
Logger.error('AuthService', 'endSignoutMainWindow error', err?.message);
}
}
}