Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions __test__/auth/CompositeLogOutHandler.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import CompositeLogOutHandler from "../../src/features/auth/domain/logOut/CompositeLogOutHandler"

test("It invokes all log out handlers", async () => {
let didCallLogOutHandler1 = false
let didCallLogOutHandler2 = false
let didCallLogOutHandler3 = false
const sut = new CompositeLogOutHandler([{
async handleLogOut() {
didCallLogOutHandler1 = true
}
}, {
async handleLogOut() {
didCallLogOutHandler2 = true
}
}, {
async handleLogOut() {
didCallLogOutHandler3 = true
}
}])
await sut.handleLogOut()
expect(didCallLogOutHandler1).toBeTruthy()
expect(didCallLogOutHandler2).toBeTruthy()
expect(didCallLogOutHandler3).toBeTruthy()
})
11 changes: 11 additions & 0 deletions __test__/auth/ErrorIgnoringLogOutHandler.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import ErrorIgnoringLogOutHandler from "../../src/features/auth/domain/logOut/ErrorIgnoringLogOutHandler"

test("It ignores errors", async () => {
const sut = new ErrorIgnoringLogOutHandler({
async handleLogOut() {
throw new Error("Mock")
}
})
// Test will fail if the following throws.
await sut.handleLogOut()
})
16 changes: 16 additions & 0 deletions __test__/auth/UserDataCleanUpLogOutHandler.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import UserDataCleanUpLogOutHandler from "../../src/features/auth/domain/logOut/UserDataCleanUpLogOutHandler"

test("It deletes data for the read user ID", async () => {
let deletedUserId: string | undefined
const sut = new UserDataCleanUpLogOutHandler({
async getUserId() {
return "foo"
},
}, {
async delete(userId) {
deletedUserId = userId
}
})
await sut.handleLogOut()
expect(deletedUserId).toBe("foo")
})
41 changes: 0 additions & 41 deletions __test__/common/authHandler/logoutHandler.test.ts

This file was deleted.

18 changes: 4 additions & 14 deletions src/app/api/auth/[auth0]/route.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
import { NextRequest, NextResponse } from "next/server"
import { NextResponse } from "next/server"
import {
handleAuth,
handleCallback,
handleLogout,
AfterCallbackAppRoute,
NextAppRouterHandler,
AppRouteHandlerFnContext,
AppRouterOnError
} from "@auth0/nextjs-auth0"
import {
initialOAuthTokenService,
sessionOAuthTokenRepository,
projectRepository,
logoutHandler
} from "@/composition"
import { initialOAuthTokenService, logOutHandler } from "@/composition"

const { SHAPE_DOCS_BASE_URL } = process.env

Expand All @@ -27,12 +21,8 @@ const onError: AppRouterOnError = async () => {
return NextResponse.redirect(url)
}

const onLogout: NextAppRouterHandler = async (req: NextRequest, ctx: AppRouteHandlerFnContext) => {
await Promise.all([
sessionOAuthTokenRepository.deleteOAuthToken().catch(() => null),
projectRepository.delete().catch(() => null)
])
await logoutHandler()
const onLogout: NextAppRouterHandler = async (req, ctx) => {
await logOutHandler.handleLogOut()
return await handleLogout(req, ctx)
}

Expand Down
12 changes: 0 additions & 12 deletions src/common/authHandler/logout.ts

This file was deleted.

31 changes: 20 additions & 11 deletions src/composition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import AccessTokenRefreshingGitHubClient from "@/common/github/AccessTokenRefres
import Auth0RefreshTokenReader from "@/features/auth/data/Auth0RefreshTokenReader"
import Auth0Session from "@/common/session/Auth0Session"
import CachingProjectDataSource from "@/features/projects/domain/CachingProjectDataSource"
import CompositeLogOutHandler from "@/features/auth/domain/logOut/CompositeLogOutHandler"
import ErrorIgnoringLogOutHandler from "@/features/auth/domain/logOut/ErrorIgnoringLogOutHandler"
import GitHubClient from "@/common/github/GitHubClient"
import GitHubOAuthTokenRefresher from "@/features/auth/data/GitHubOAuthTokenRefresher"
import GitHubOrganizationSessionValidator from "@/common/session/GitHubOrganizationSessionValidator"
Expand All @@ -18,7 +20,7 @@ import SessionMutexFactory from "@/common/mutex/SessionMutexFactory"
import SessionOAuthTokenRepository from "@/features/auth/domain/SessionOAuthTokenRepository"
import SessionValidatingProjectDataSource from "@/features/projects/domain/SessionValidatingProjectDataSource"
import OAuthTokenRepository from "@/features/auth/domain/OAuthTokenRepository"
import authLogoutHandler from "@/common/authHandler/logout"
import UserDataCleanUpLogOutHandler from "@/features/auth/domain/logOut/UserDataCleanUpLogOutHandler"

const {
AUTH0_MANAGEMENT_DOMAIN,
Expand All @@ -34,13 +36,15 @@ const {

const gitHubPrivateKey = Buffer.from(GITHUB_PRIVATE_KEY_BASE_64, "base64").toString("utf-8")

const session = new Auth0Session()

const oAuthTokenRepository = new KeyValueUserDataRepository(
new RedisKeyValueStore(REDIS_URL),
"authToken"
)

export const sessionOAuthTokenRepository = new SessionOAuthTokenRepository(
new SessionDataRepository(new Auth0Session(), oAuthTokenRepository)
new SessionDataRepository(session, oAuthTokenRepository)
)

const gitHubOAuthTokenRefresher = new GitHubOAuthTokenRefresher({
Expand All @@ -55,7 +59,7 @@ export const gitHubClient = new AccessTokenRefreshingGitHubClient(
new LockingAccessTokenRefresher(
new SessionMutexFactory(
new RedisKeyedMutexFactory(REDIS_URL),
new Auth0Session(),
session,
"mutexAccessToken"
),
sessionOAuthTokenRepository,
Expand All @@ -77,12 +81,14 @@ export const sessionValidator = new GitHubOrganizationSessionValidator(
GITHUB_ORGANIZATION_NAME
)

const projectUserDataRepository = new KeyValueUserDataRepository(
new RedisKeyValueStore(REDIS_URL),
"projects"
)

export const projectRepository = new ProjectRepository(
new Auth0Session(),
new KeyValueUserDataRepository(
new RedisKeyValueStore(REDIS_URL),
"projects"
)
session,
projectUserDataRepository
)

export const projectDataSource = new CachingProjectDataSource(
Expand All @@ -107,6 +113,9 @@ export const initialOAuthTokenService = new InitialOAuthTokenService({
oAuthTokenRepository: new OAuthTokenRepository(oAuthTokenRepository)
})

export const logoutHandler = async () => {
await authLogoutHandler(sessionOAuthTokenRepository, projectRepository)
}
export const logOutHandler = new ErrorIgnoringLogOutHandler(
new CompositeLogOutHandler([
new UserDataCleanUpLogOutHandler(session, projectUserDataRepository),
new UserDataCleanUpLogOutHandler(session, oAuthTokenRepository)
])
)
14 changes: 14 additions & 0 deletions src/features/auth/domain/logOut/CompositeLogOutHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import ILogOutHandler from "./ILogOutHandler"

export default class CompositeLogOutHandler implements ILogOutHandler {
private readonly handlers: ILogOutHandler[]

constructor(handlers: ILogOutHandler[]) {
this.handlers = handlers
}

async handleLogOut(): Promise<void> {
const promises = this.handlers.map(e => e.handleLogOut())
await Promise.all(promises)
}
}
17 changes: 17 additions & 0 deletions src/features/auth/domain/logOut/ErrorIgnoringLogOutHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import ILogOutHandler from "./ILogOutHandler"

export default class ErrorIgnoringLogOutHandler implements ILogOutHandler {
private readonly handler: ILogOutHandler

constructor(handler: ILogOutHandler) {
this.handler = handler
}

async handleLogOut(): Promise<void> {
try {
await this.handler.handleLogOut()
} catch {
// We intentionally do not handle errors.
}
}
}
3 changes: 3 additions & 0 deletions src/features/auth/domain/logOut/ILogOutHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default interface ILogOutHandler {
handleLogOut(): Promise<void>
}
24 changes: 24 additions & 0 deletions src/features/auth/domain/logOut/UserDataCleanUpLogOutHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import ILogOutHandler from "./ILogOutHandler"

interface IUserIDReader {
getUserId(): Promise<string>
}

interface Repository {
delete(userId: string): Promise<void>
}

export default class UserDataCleanUpLogOutHandler implements ILogOutHandler {
private readonly userIdReader: IUserIDReader
private readonly repository: Repository

constructor(userIdReader: IUserIDReader, repository: Repository) {
this.userIdReader = userIdReader
this.repository = repository
}

async handleLogOut(): Promise<void> {
const userId = await this.userIdReader.getUserId()
return await this.repository.delete(userId)
}
}