diff --git a/__test__/auth/InitialOAuthTokenService.test.ts b/__test__/auth/CredentialsTransferrer.test.ts similarity index 81% rename from __test__/auth/InitialOAuthTokenService.test.ts rename to __test__/auth/CredentialsTransferrer.test.ts index 307b61f6..7723e8c1 100644 --- a/__test__/auth/InitialOAuthTokenService.test.ts +++ b/__test__/auth/CredentialsTransferrer.test.ts @@ -1,9 +1,9 @@ -import InitialOAuthTokenService from "../../src/features/auth/domain/oAuthToken/InitialOAuthTokenService" +import CredentialsTransferrer from "../../src/features/auth/domain/credentialsTransfer/CredentialsTransferrer" import OAuthToken from "../../src/features/auth/domain/oAuthToken/OAuthToken" test("It fetches refresh token for specified user", async () => { let fetchedUserId: string | undefined - const sut = new InitialOAuthTokenService({ + const sut = new CredentialsTransferrer({ refreshTokenReader: { async getRefreshToken(userId) { fetchedUserId = userId @@ -23,13 +23,13 @@ test("It fetches refresh token for specified user", async () => { async delete() {} } }) - await sut.fetchInitialAuthTokenForUser("123") + await sut.transferCredentials("123") expect(fetchedUserId).toBe("123") }) test("It refreshes the fetched refresh token", async () => { let refreshedRefreshToken: string | undefined - const sut = new InitialOAuthTokenService({ + const sut = new CredentialsTransferrer({ refreshTokenReader: { async getRefreshToken() { return "helloworld" @@ -49,14 +49,14 @@ test("It refreshes the fetched refresh token", async () => { async delete() {} } }) - await sut.fetchInitialAuthTokenForUser("123") + await sut.transferCredentials("123") expect(refreshedRefreshToken).toBe("helloworld") }) -test("It stores the refreshed auth token for the correct user ID", async () => { +test("It stores the refreshed auth token for the user", async () => { let storedAuthToken: OAuthToken | undefined let storedUserId: string | undefined - const sut = new InitialOAuthTokenService({ + const sut = new CredentialsTransferrer({ refreshTokenReader: { async getRefreshToken() { return "helloworld" @@ -78,7 +78,7 @@ test("It stores the refreshed auth token for the correct user ID", async () => { async delete() {} } }) - await sut.fetchInitialAuthTokenForUser("123") + await sut.transferCredentials("123") expect(storedAuthToken?.accessToken).toBe("foo") expect(storedAuthToken?.refreshToken).toBe("bar") expect(storedUserId).toBe("123") diff --git a/__test__/auth/CredentialsTransferringLogInHandler.test.ts b/__test__/auth/CredentialsTransferringLogInHandler.test.ts new file mode 100644 index 00000000..90b106fe --- /dev/null +++ b/__test__/auth/CredentialsTransferringLogInHandler.test.ts @@ -0,0 +1,12 @@ +import CredentialsTransferringLogInHandler from "../../src/features/auth/domain/logIn/CredentialsTransferringLogInHandler" + +test("It transfers credentials", async () => { + let didTransferCredentials = false + const sut = new CredentialsTransferringLogInHandler({ + async transferCredentials() { + didTransferCredentials = true + } + }) + await sut.handleLogIn("1234") + expect(didTransferCredentials).toBeTruthy() +}) diff --git a/src/app/api/auth/[auth0]/route.ts b/src/app/api/auth/[auth0]/route.ts index b8e11a5e..aeaf8b83 100644 --- a/src/app/api/auth/[auth0]/route.ts +++ b/src/app/api/auth/[auth0]/route.ts @@ -7,12 +7,12 @@ import { NextAppRouterHandler, AppRouterOnError } from "@auth0/nextjs-auth0" -import { initialOAuthTokenService, logOutHandler } from "@/composition" +import { logInHandler, logOutHandler } from "@/composition" const { SHAPE_DOCS_BASE_URL } = process.env const afterCallback: AfterCallbackAppRoute = async (_req, session) => { - await initialOAuthTokenService.fetchInitialAuthTokenForUser(session.user.sub) + await logInHandler.handleLogIn(session.user.sub) return session } diff --git a/src/composition.ts b/src/composition.ts index 077cb29f..916d5814 100644 --- a/src/composition.ts +++ b/src/composition.ts @@ -4,12 +4,13 @@ import Auth0RefreshTokenReader from "@/features/auth/data/Auth0RefreshTokenReade import Auth0Session from "@/common/session/Auth0Session" import CachingProjectDataSource from "@/features/projects/domain/CachingProjectDataSource" import CompositeLogOutHandler from "@/features/auth/domain/logOut/CompositeLogOutHandler" +import CredentialsTransferrer from "@/features/auth/domain/credentialsTransfer/CredentialsTransferrer" +import CredentialsTransferringLogInHandler from "@/features/auth/domain/logIn/CredentialsTransferringLogInHandler" 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" import GitHubProjectDataSource from "@/features/projects/data/GitHubProjectDataSource" -import InitialOAuthTokenService from "@/features/auth/domain/oAuthToken/InitialOAuthTokenService" import KeyValueUserDataRepository from "@/common/userData/KeyValueUserDataRepository" import LockingAccessTokenService from "@/features/auth/domain/accessToken/LockingAccessTokenService" import OnlyStaleRefreshingAccessTokenService from "@/features/auth/domain/accessToken/OnlyStaleRefreshingAccessTokenService" @@ -99,19 +100,21 @@ export const projectDataSource = new CachingProjectDataSource( projectRepository ) -export const initialOAuthTokenService = new InitialOAuthTokenService({ - refreshTokenReader: new Auth0RefreshTokenReader({ - domain: AUTH0_MANAGEMENT_DOMAIN, - clientId: AUTH0_MANAGEMENT_CLIENT_ID, - clientSecret: AUTH0_MANAGEMENT_CLIENT_SECRET, - connection: "github" - }), - oAuthTokenRefresher: new GitHubOAuthTokenRefresher({ - clientId: GITHUB_CLIENT_ID, - clientSecret: GITHUB_CLIENT_SECRET - }), - oAuthTokenRepository: oAuthTokenRepository -}) +export const logInHandler = new CredentialsTransferringLogInHandler( + new CredentialsTransferrer({ + refreshTokenReader: new Auth0RefreshTokenReader({ + domain: AUTH0_MANAGEMENT_DOMAIN, + clientId: AUTH0_MANAGEMENT_CLIENT_ID, + clientSecret: AUTH0_MANAGEMENT_CLIENT_SECRET, + connection: "github" + }), + oAuthTokenRefresher: new GitHubOAuthTokenRefresher({ + clientId: GITHUB_CLIENT_ID, + clientSecret: GITHUB_CLIENT_SECRET + }), + oAuthTokenRepository: oAuthTokenRepository + }) +) export const logOutHandler = new ErrorIgnoringLogOutHandler( new CompositeLogOutHandler([ diff --git a/src/features/auth/domain/credentialsTransfer/CredentialsTransferrer.ts b/src/features/auth/domain/credentialsTransfer/CredentialsTransferrer.ts new file mode 100644 index 00000000..c061a2cb --- /dev/null +++ b/src/features/auth/domain/credentialsTransfer/CredentialsTransferrer.ts @@ -0,0 +1,31 @@ +import IOAuthTokenRefresher from "../oAuthToken/IOAuthTokenRefresher" +import IOAuthTokenRepository from "../oAuthToken/IOAuthTokenRepository" +import ICredentialsTransferrer from "./ICredentialsTransferrer" + +export interface IRefreshTokenReader { + getRefreshToken(userId: string): Promise +} + +type CredentialsTransferrerConfig = { + readonly refreshTokenReader: IRefreshTokenReader + readonly oAuthTokenRefresher: IOAuthTokenRefresher + readonly oAuthTokenRepository: IOAuthTokenRepository +} + +export default class CredentialsTransferrer implements ICredentialsTransferrer { + private readonly refreshTokenReader: IRefreshTokenReader + private readonly oAuthTokenRefresher: IOAuthTokenRefresher + private readonly oAuthTokenRepository: IOAuthTokenRepository + + constructor(config: CredentialsTransferrerConfig) { + this.refreshTokenReader = config.refreshTokenReader + this.oAuthTokenRefresher = config.oAuthTokenRefresher + this.oAuthTokenRepository = config.oAuthTokenRepository + } + + async transferCredentials(userId: string): Promise { + const refreshToken = await this.refreshTokenReader.getRefreshToken(userId) + const authToken = await this.oAuthTokenRefresher.refreshOAuthToken(refreshToken) + await this.oAuthTokenRepository.set(userId, authToken) + } +} diff --git a/src/features/auth/domain/credentialsTransfer/ICredentialsTransferrer.ts b/src/features/auth/domain/credentialsTransfer/ICredentialsTransferrer.ts new file mode 100644 index 00000000..7bea8973 --- /dev/null +++ b/src/features/auth/domain/credentialsTransfer/ICredentialsTransferrer.ts @@ -0,0 +1,3 @@ +export default interface ICredentialsTransferrer { + transferCredentials(userId: string): Promise +} diff --git a/src/features/auth/domain/logIn/CredentialsTransferringLogInHandler.ts b/src/features/auth/domain/logIn/CredentialsTransferringLogInHandler.ts new file mode 100644 index 00000000..618b2713 --- /dev/null +++ b/src/features/auth/domain/logIn/CredentialsTransferringLogInHandler.ts @@ -0,0 +1,18 @@ +import ICredentialsTransferrer from "../credentialsTransfer/ICredentialsTransferrer" +import ILogInHandler from "./ILogInHandler" + +export interface IRefreshTokenReader { + getRefreshToken(userId: string): Promise +} + +export default class CredentialsTransferringLogInHandler implements ILogInHandler { + private readonly credentialsTransferrer: ICredentialsTransferrer + + constructor(credentialsTransferrer: ICredentialsTransferrer) { + this.credentialsTransferrer = credentialsTransferrer + } + + async handleLogIn(userId: string): Promise { + await this.credentialsTransferrer.transferCredentials(userId) + } +} diff --git a/src/features/auth/domain/logIn/ILogInHandler.ts b/src/features/auth/domain/logIn/ILogInHandler.ts new file mode 100644 index 00000000..b0e0e0f4 --- /dev/null +++ b/src/features/auth/domain/logIn/ILogInHandler.ts @@ -0,0 +1,3 @@ +export default interface ILogInHandler { + handleLogIn(userId: string): Promise +} diff --git a/src/features/auth/domain/oAuthToken/InitialOAuthTokenService.ts b/src/features/auth/domain/oAuthToken/InitialOAuthTokenService.ts deleted file mode 100644 index c75bd8e2..00000000 --- a/src/features/auth/domain/oAuthToken/InitialOAuthTokenService.ts +++ /dev/null @@ -1,26 +0,0 @@ -import IOAuthTokenRefresher from "./IOAuthTokenRefresher" -import IOAuthTokenRepository from "./IOAuthTokenRepository" - -interface IRefreshTokenReader { - getRefreshToken(userId: string): Promise -} - -type InitialOAuthTokenServiceConfig = { - readonly refreshTokenReader: IRefreshTokenReader - readonly oAuthTokenRefresher: IOAuthTokenRefresher - readonly oAuthTokenRepository: IOAuthTokenRepository -} - -export default class InitialOAuthTokenService { - private readonly config: InitialOAuthTokenServiceConfig - - constructor(config: InitialOAuthTokenServiceConfig) { - this.config = config - } - - async fetchInitialAuthTokenForUser(userId: string): Promise { - const refreshToken = await this.config.refreshTokenReader.getRefreshToken(userId) - const authToken = await this.config.oAuthTokenRefresher.refreshOAuthToken(refreshToken) - this.config.oAuthTokenRepository.set(userId, authToken) - } -} \ No newline at end of file