-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2083 from navikt/feature/webworker-login
[FAGSYSTEM-304086] Flytte login logikk til webworker
- Loading branch information
Showing
12 changed files
with
364 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
export enum FeatureToggles { | ||
BrukSoknadsstatus = 'modiapersonoversikt.soknadsstatus-api', | ||
BrukUtvidetUtbetalingsSporring = 'modiapersonoversikt.utvidet-utbetalings-sporring', | ||
BrukWebworkerPaaInnLogging = 'modiapersonoversikt.web-worker-på-innlogging', | ||
DebugMeldingsFunksjonalitet = 'modiapersonoversikt.ny-send-melding-container' | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
export class ActivityMonitor { | ||
private lastActivity: number = new Date().getTime(); | ||
|
||
public update() { | ||
this.lastActivity = new Date().getTime(); | ||
} | ||
|
||
public timeSinceLastActivity(): number { | ||
return new Date().getTime() - this.lastActivity; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import { AuthIntropectionDTO } from '../utils/hooks/use-persistent-login'; | ||
import { ActivityMonitor } from './AcitivityMonitor'; | ||
import { INACTIVITY_LIMIT_IN_MS, PREEMPTIVE_REFRESH_TIME_IN_MS, RECALC_LOGIN_STATUS_INTERVAL_IN_MS } from './constants'; | ||
import { timeToExpiration } from './timeToExpiration'; | ||
|
||
export class LoginStateManager { | ||
private timeout: ReturnType<typeof setTimeout> | null = null; | ||
private interval: ReturnType<typeof setInterval> | null = null; | ||
private activityMonitor = new ActivityMonitor(); | ||
private _refreshToken?: () => void; | ||
private _onLoginStateUpdate?: (props: { isLoggedIn: boolean }) => void; | ||
|
||
initialize = (refreshToken: () => void, onLoginStateUpdate: (props: { isLoggedIn: boolean }) => void) => { | ||
this._refreshToken = refreshToken; | ||
this._onLoginStateUpdate = onLoginStateUpdate; | ||
}; | ||
|
||
private setupTokenRefresher = (timeToRefresh: number) => { | ||
this.timeout = this.getTokenRefreshTimeout(this.activityMonitor, timeToRefresh); | ||
}; | ||
|
||
private get refreshToken() { | ||
if (!this._refreshToken) { | ||
throw new Error('[LoginStateManager] var ikke initialisert med en metode for å refreshe token'); | ||
} | ||
return this._refreshToken; | ||
} | ||
|
||
private get onLoginStateUpdate() { | ||
if (!this._onLoginStateUpdate) { | ||
throw new Error('[LoginStateManager] var ikke initialisert med en metode for å sende login oppdateringer'); | ||
} | ||
return this._onLoginStateUpdate; | ||
} | ||
|
||
private getTokenRefreshTimeout = (activityMonitor: ActivityMonitor, timeToExpiration: number) => { | ||
return setTimeout(() => { | ||
if (activityMonitor.timeSinceLastActivity() < INACTIVITY_LIMIT_IN_MS) { | ||
if (!this.refreshToken) { | ||
throw new Error('[LoginStateManager] Var ikke initialisert med '); | ||
} | ||
if (this.refreshToken) { | ||
this.refreshToken(); | ||
} | ||
} | ||
}, [timeToExpiration - PREEMPTIVE_REFRESH_TIME_IN_MS]); | ||
}; | ||
|
||
private getLoginStateInterval = (auth: AuthIntropectionDTO) => { | ||
return setInterval(() => { | ||
const timeLeft = timeToExpiration(auth.expirationDate); | ||
if (this.onLoginStateUpdate) { | ||
this.onLoginStateUpdate({ isLoggedIn: timeLeft > 0 }); | ||
} | ||
}, RECALC_LOGIN_STATUS_INTERVAL_IN_MS); | ||
}; | ||
|
||
private onAuthStateUpdate = (auth: AuthIntropectionDTO) => { | ||
this.stopTokenRefresher(); | ||
const timeToRefresh = timeToExpiration(auth.expirationDate); | ||
if (timeToRefresh === 0) { | ||
if (this.refreshToken) { | ||
this.refreshToken(); | ||
} | ||
return; | ||
} | ||
this.setupTokenRefresher(timeToRefresh); | ||
}; | ||
|
||
private setupLoginStateNotifier = (auth: AuthIntropectionDTO) => { | ||
this.stopLoginStateNotifier(); | ||
this.interval = this.getLoginStateInterval(auth); | ||
}; | ||
|
||
private stopTokenRefresher = () => { | ||
if (this.timeout) { | ||
clearTimeout(this.timeout); | ||
} | ||
}; | ||
|
||
private stopLoginStateNotifier = () => { | ||
if (this.interval) { | ||
clearInterval(this.interval); | ||
} | ||
}; | ||
|
||
onUserActive = () => { | ||
this.activityMonitor.update(); | ||
}; | ||
|
||
onUpdate = (auth: AuthIntropectionDTO) => { | ||
this.onAuthStateUpdate(auth); | ||
this.setupLoginStateNotifier(auth); | ||
}; | ||
|
||
stopWork = () => { | ||
this.stopTokenRefresher(); | ||
this.stopLoginStateNotifier(); | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import { AuthIntropectionDTO } from '../utils/hooks/use-persistent-login'; | ||
import { LoginStateManager } from './LoginStateManager'; | ||
import { META_URL } from './metaUrl'; | ||
import { WWMessage, OutgoingMessageType, IncommingMessageType } from './types'; | ||
|
||
export interface IWebWorkerCom { | ||
initialize: (refreshToken: () => void, onLoginStateUpdate: (props: { isLoggedIn: boolean }) => void) => void; | ||
onAuthChange: (newState: AuthIntropectionDTO) => void; | ||
stop: () => void; | ||
onUserActive: () => void; | ||
} | ||
|
||
export class WebWorkerCommunicator implements IWebWorkerCom { | ||
private worker: Worker; | ||
refreshToken?: () => void; | ||
onLoginStateUpdate?: (props: { isLoggedIn: boolean }) => void; | ||
|
||
constructor() { | ||
this.worker = new window.Worker(new URL('../loginWebWorker', META_URL)); | ||
} | ||
|
||
initialize = (refreshToken: () => void, onLoginStateUpdate: (props: { isLoggedIn: boolean }) => void) => { | ||
this.refreshToken = refreshToken; | ||
this.onLoginStateUpdate = onLoginStateUpdate; | ||
this.worker.onmessage = (message: MessageEvent<WWMessage<any>>) => { | ||
const { type, payload } = message.data; | ||
this.onMessage(type as OutgoingMessageType, payload); | ||
}; | ||
}; | ||
|
||
private sendMessage = (type: IncommingMessageType, payload?: any) => { | ||
const message: WWMessage<any> = { | ||
type, | ||
payload | ||
}; | ||
this.worker.postMessage(message); | ||
}; | ||
|
||
private onMessage = (type: OutgoingMessageType, payload?: any) => { | ||
switch (type) { | ||
case 'REFRESH_TOKEN': { | ||
if (!this.refreshToken) { | ||
throw new Error('WebWorker was not initialized before being called'); | ||
} | ||
this.refreshToken(); | ||
return; | ||
} | ||
case 'LOGIN_STATE_UPDATE': { | ||
if (!this.onLoginStateUpdate) { | ||
throw new Error('WebWorker was not initialized before being called'); | ||
} | ||
this.onLoginStateUpdate({ isLoggedIn: payload }); | ||
return; | ||
} | ||
} | ||
}; | ||
|
||
onUserActive = () => { | ||
this.sendMessage('USER_ACTIVE'); | ||
}; | ||
|
||
onAuthChange = (newState: AuthIntropectionDTO) => { | ||
this.sendMessage('AUTH_STATE_UPDATE', newState); | ||
}; | ||
|
||
stop = () => this.sendMessage('STOP_WORKER'); | ||
} | ||
|
||
export class NoWorkerCommunicator implements IWebWorkerCom { | ||
private loginStateManager = new LoginStateManager(); | ||
|
||
initialize = (refreshToken: () => void, onLoginStateUpdate: (props: { isLoggedIn: boolean }) => void) => { | ||
this.loginStateManager.initialize(refreshToken, onLoginStateUpdate); | ||
}; | ||
|
||
onAuthChange = (newState: AuthIntropectionDTO) => { | ||
this.loginStateManager.onUpdate(newState); | ||
}; | ||
stop = () => { | ||
this.loginStateManager.stopWork(); | ||
}; | ||
|
||
onUserActive = () => { | ||
this.loginStateManager.onUserActive(); | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
export const SECOND_IN_MS = 1000; | ||
export const MINUTE_IN_MS = 60 * SECOND_IN_MS; | ||
|
||
export const RECALC_LOGIN_STATUS_INTERVAL_IN_MS = 30 * SECOND_IN_MS; | ||
export const INACTIVITY_LIMIT_IN_MS = 10 * MINUTE_IN_MS; | ||
export const PREEMPTIVE_REFRESH_TIME_IN_MS = 120 * SECOND_IN_MS; | ||
export const ESTIMATED_EXPIRATION_IN_MS = 3600 * SECOND_IN_MS; | ||
export const INVALID_EXPIRATION_DATE = -1; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
// Jest doesn't support import.meta, so this is extracted here to mock it during tests. | ||
export const META_URL = import.meta.url; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { ESTIMATED_EXPIRATION_IN_MS, INVALID_EXPIRATION_DATE } from './constants'; | ||
|
||
export const timeToExpiration = (expirationDate: number): number => { | ||
if (expirationDate === INVALID_EXPIRATION_DATE) { | ||
return ESTIMATED_EXPIRATION_IN_MS; | ||
} | ||
const currentDate = new Date().getTime(); | ||
return expirationDate - currentDate; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
export type OutgoingMessageType = 'LOGIN_STATE_UPDATE' | 'REFRESH_TOKEN'; | ||
export type IncommingMessageType = 'AUTH_STATE_UPDATE' | 'STOP_WORKER' | 'USER_ACTIVE'; | ||
export interface WWMessage<T> { | ||
type: IncommingMessageType | OutgoingMessageType; | ||
payload: T; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
/* eslint-disable no-restricted-globals */ | ||
import { LoginStateManager } from './login/LoginStateManager'; | ||
import { IncommingMessageType, OutgoingMessageType } from './login/types'; | ||
|
||
const loginStateManager = new LoginStateManager(); | ||
|
||
const register = () => { | ||
console.log('Bruker webworker for å kontrollere inlogging'); | ||
self.addEventListener('message', handleEventMessage); | ||
}; | ||
|
||
const handleEventMessage = (event: MessageEvent<{ type: IncommingMessageType; payload: any }>) => { | ||
const { type, payload } = event.data; | ||
switch (type) { | ||
case 'STOP_WORKER': | ||
loginStateManager.stopWork(); | ||
console.log('[loginWebWorker] Mottok melding: ', type); | ||
return; | ||
case 'AUTH_STATE_UPDATE': | ||
loginStateManager.onUpdate(payload); | ||
console.log(`[loginWebWorker] Mottok melding: ${type}, med: ${payload.expirationDate}`); | ||
return; | ||
case 'USER_ACTIVE': | ||
loginStateManager.onUserActive(); | ||
return; | ||
} | ||
}; | ||
|
||
const sendRefreshMessage = () => { | ||
sendMessage('REFRESH_TOKEN'); | ||
}; | ||
|
||
const sendIsLoggedIn = ({ isLoggedIn }: { isLoggedIn: boolean }) => { | ||
sendMessage('LOGIN_STATE_UPDATE', isLoggedIn); | ||
}; | ||
|
||
const sendMessage = <T>(type: OutgoingMessageType, payload?: T) => { | ||
self.postMessage({ type, payload }); | ||
}; | ||
|
||
loginStateManager.initialize(sendRefreshMessage, sendIsLoggedIn); | ||
|
||
register(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.