Support periodic auth checks in SecurityContext on web socket connections
This commit is contained in:
@@ -80,6 +80,9 @@ export abstract class SecurityContext {
|
||||
/**
|
||||
* Assuming a user is still authenticated in the context,
|
||||
* try to look up and fill in the user.
|
||||
*
|
||||
* If there is NO USER to be resumed, then the method should flush
|
||||
* the user from this context.
|
||||
*/
|
||||
abstract resume(): Awaitable<void>
|
||||
|
||||
|
||||
@@ -33,7 +33,10 @@ export class SessionSecurityContext extends SecurityContext {
|
||||
if ( user ) {
|
||||
this.authenticatedUser = user
|
||||
await this.bus.push(new UserAuthenticationResumedEvent(user, this))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
this.authenticatedUser = undefined
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,9 @@ export class TokenSecurityContext extends SecurityContext {
|
||||
if ( user ) {
|
||||
this.authenticatedUser = user
|
||||
await this.bus.push(new UserAuthenticationResumedEvent(user, this))
|
||||
return
|
||||
}
|
||||
|
||||
this.authenticatedUser = undefined
|
||||
}
|
||||
}
|
||||
|
||||
27
src/auth/event/AuthCheckFailed.ts
Normal file
27
src/auth/event/AuthCheckFailed.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import {BaseEvent, BaseSerializer, ObjectSerializer} from '../../support/bus'
|
||||
import {Awaitable} from '../../util'
|
||||
|
||||
/** An event raised when a required auth check has failed. */
|
||||
export class AuthCheckFailed extends BaseEvent {
|
||||
eventName = '@extollo/lib:AuthCheckFailed'
|
||||
}
|
||||
|
||||
/** Serializes AuthCheckFailed events. */
|
||||
@ObjectSerializer()
|
||||
export class AuthCheckFailedSerializer extends BaseSerializer<AuthCheckFailed, { authCheckFailed: true }> {
|
||||
protected decodeSerial(): Awaitable<AuthCheckFailed> {
|
||||
return new AuthCheckFailed()
|
||||
}
|
||||
|
||||
protected encodeActual(): Awaitable<{ authCheckFailed: true }> {
|
||||
return { authCheckFailed: true }
|
||||
}
|
||||
|
||||
protected getName(): string {
|
||||
return '@extollo/lib:AuthCheckFailedSerializer'
|
||||
}
|
||||
|
||||
matchActual(some: AuthCheckFailed): boolean {
|
||||
return some instanceof AuthCheckFailed
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ export * from './event/AuthenticationEvent'
|
||||
export * from './event/UserAuthenticatedEvent'
|
||||
export * from './event/UserAuthenticationResumedEvent'
|
||||
export * from './event/UserFlushedEvent'
|
||||
export * from './event/AuthCheckFailed'
|
||||
|
||||
export * from './middleware/AuthRequiredMiddleware'
|
||||
export * from './middleware/GuestRequiredMiddleware'
|
||||
@@ -33,6 +34,8 @@ export * from './repository/orm/ORMUserRepository'
|
||||
|
||||
export * from './config'
|
||||
|
||||
export * from './webSocketAuthCheck'
|
||||
|
||||
export * from './server/types'
|
||||
export * from './server/models/OAuth2TokenModel'
|
||||
export * from './server/repositories/ConfigClientRepository'
|
||||
|
||||
36
src/auth/webSocketAuthCheck.ts
Normal file
36
src/auth/webSocketAuthCheck.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import {Container} from '../di'
|
||||
import {RequestLocalStorage} from '../http/RequestLocalStorage'
|
||||
import {Session} from '../http/session/Session'
|
||||
import {Logging} from '../service/Logging'
|
||||
import {SecurityContext} from './context/SecurityContext'
|
||||
import {Bus} from '../support/bus'
|
||||
import {AuthCheckFailed} from './event/AuthCheckFailed'
|
||||
|
||||
/**
|
||||
* Check if the security context for the current request's web socket is still valid.
|
||||
* If not, raise an `AuthCheckFailed` event. This is meant to be used as a subscriber
|
||||
* to `WebSocketHealthCheckEvent` on the request.
|
||||
*
|
||||
* @see AuthCheckFailed
|
||||
*/
|
||||
export async function webSocketAuthCheck(): Promise<void> {
|
||||
const request = Container.getContainer()
|
||||
.make<RequestLocalStorage>(RequestLocalStorage)
|
||||
.get()
|
||||
|
||||
const logging = request.make<Logging>(Logging)
|
||||
|
||||
try {
|
||||
// Try to re-load the session in case we're using the SessionSecurityContext
|
||||
await request.make<Session>(Session).load()
|
||||
} catch (e: unknown) {
|
||||
logging.error(e)
|
||||
}
|
||||
|
||||
const security = request.make<SecurityContext>(SecurityContext)
|
||||
await security.resume()
|
||||
|
||||
if ( !security.hasUser() ) {
|
||||
await request.make<Bus>(Bus).push(new AuthCheckFailed())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user