Skip to content

Commit 55258e1

Browse files
committed
Stores access token and refresh token obtained at login
1 parent b01dc3a commit 55258e1

File tree

8 files changed

+71
-215
lines changed

8 files changed

+71
-215
lines changed

__test__/auth/CompositeOAuthTokenRepository.test.ts

Lines changed: 0 additions & 143 deletions
This file was deleted.

src/common/utils/saneParseInt.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export default (str: string) => {
2+
const num = parseInt(str, 10)
3+
if (isNaN(num) || str.trim() !== num.toString()) {
4+
return undefined
5+
}
6+
return num
7+
}

src/composition.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ import {
2929
AccountProviderTypeBasedOAuthTokenRefresher,
3030
AuthjsAccountsOAuthTokenRepository,
3131
CompositeLogOutHandler,
32-
CompositeOAuthTokenRepository,
3332
ErrorIgnoringLogOutHandler,
33+
FallbackOAuthTokenRepository,
3434
GitHubOrganizationSessionValidator,
3535
GuestOAuthTokenDataSource,
3636
GuestOAuthTokenRefresher,
@@ -92,18 +92,16 @@ const pool = new Pool({
9292

9393
const db = new PostgreSQLDB({ pool })
9494

95-
const oauthTokenRepository = new CompositeOAuthTokenRepository({
96-
oAuthTokenRepositories: [
97-
new OAuthTokenRepository({ db, provider: "github" }),
98-
new AuthjsAccountsOAuthTokenRepository({ db, provider: "github" })
99-
]
95+
const oauthTokenRepository = new FallbackOAuthTokenRepository({
96+
primaryRepository: new OAuthTokenRepository({ db, provider: "github" }),
97+
secondaryRepository: new AuthjsAccountsOAuthTokenRepository({ db, provider: "github" })
10098
})
10199

102100
const userRepository: IUserRepository = new DbUserRepository({ db })
103101

104102
export const guestRepository: IGuestRepository = new DbGuestRepository({ db })
105103

106-
const logInHandler = new LogInHandler({ userRepository, guestRepository })
104+
const logInHandler = new LogInHandler({ userRepository, guestRepository, oauthTokenRepository })
107105

108106
// Must be a verified email in AWS SES.
109107
const fromEmail = FROM_EMAIL || "Shape Docs <[email protected]>"

src/features/auth/domain/log-in/LogInHandler.ts

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,29 @@
11
import { ILogInHandler, IUser, IAccount, IEmail } from "."
22
import { IGuestRepository, IUserRepository } from "@/features/admin/domain"
3+
import { IOAuthTokenRepository } from "../oauth-token"
4+
import saneParseInt from "@/common/utils/saneParseInt"
35

46
export default class LogInHandler implements ILogInHandler {
57
private readonly userRepository: IUserRepository
68
private readonly guestRepository: IGuestRepository
9+
private readonly oauthTokenRepository: IOAuthTokenRepository
710

811
constructor(config: {
912
userRepository: IUserRepository,
10-
guestRepository: IGuestRepository
13+
guestRepository: IGuestRepository,
14+
oauthTokenRepository: IOAuthTokenRepository
1115
}) {
1216
this.userRepository = config.userRepository
1317
this.guestRepository = config.guestRepository
18+
this.oauthTokenRepository = config.oauthTokenRepository
1419
}
1520

1621
async handleLogIn(user: IUser, account: IAccount | null, email?: IEmail) {
1722
if (!account) {
1823
return false
1924
}
2025
if (account.provider === "github") {
21-
return await this.handleLogInForGitHubUser(account)
26+
return await this.handleLogInForGitHubUser(user, account)
2227
} else if (account.provider === "nodemailer") {
2328
return await this.handleLogInForGuestUser(user)
2429
} else {
@@ -27,13 +32,22 @@ export default class LogInHandler implements ILogInHandler {
2732
}
2833
}
2934

30-
private async handleLogInForGitHubUser(account: IAccount) {
31-
if (!account.access_token) {
35+
private async handleLogInForGitHubUser(user: IUser, account: IAccount) {
36+
if (!user.id) {
3237
return false
3338
}
34-
if (!account.refresh_token) {
39+
const accessToken = account.access_token
40+
const refreshToken = account.refresh_token
41+
if (!accessToken) {
3542
return false
3643
}
44+
if (!refreshToken) {
45+
return false
46+
}
47+
let userId = saneParseInt(user.id)
48+
if (userId) {
49+
await this.oauthTokenRepository.set(`${userId}`, { accessToken, refreshToken })
50+
}
3751
return true
3852
}
3953

src/features/auth/domain/oauth-token/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ export type { default as IOAuthTokenRefresher } from "./refresher/IOAuthTokenRef
88
export { default as LockingOAuthTokenRefresher } from "./refresher/LockingOAuthTokenRefresher"
99
export { default as PersistingOAuthTokenRefresher } from "./refresher/PersistingOAuthTokenRefresher"
1010
export { default as AuthjsAccountsOAuthTokenRepository } from "./repository/AuthjsAccountsOAuthTokenRepository"
11-
export { default as CompositeOAuthTokenRepository } from "./repository/CompositeOAuthTokenRepository"
11+
export { default as FallbackOAuthTokenRepository } from "./repository/FallbackOAuthTokenRepository"
1212
export type { default as IOAuthTokenRepository } from "./repository/IOAuthTokenRepository"
1313
export { default as OAuthTokenRepository } from "./repository/OAuthTokenRepository"

src/features/auth/domain/oauth-token/repository/AuthjsAccountsOAuthTokenRepository.ts

Lines changed: 2 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -30,31 +30,7 @@ export default class AuthjsAccountsOAuthTokenRepository implements IOAuthTokenRe
3030
return { accessToken, refreshToken }
3131
}
3232

33-
async set(userId: string, token: OAuthToken): Promise<void> {
34-
const query = `
35-
UPDATE accounts
36-
SET access_token = $3, refresh_token = $4
37-
WHERE provider = $1 AND \"userId\" = $2
38-
`
39-
try {
40-
await this.db.query(query, [this.provider, userId, token.accessToken, token.refreshToken])
41-
} catch (error) {
42-
console.error(error)
43-
throw error
44-
}
45-
}
33+
async set(_userId: string, _token: OAuthToken): Promise<void> {}
4634

47-
async delete(userId: string): Promise<void> {
48-
const query = `
49-
UPDATE accounts
50-
SET access_token = "", refresh_token = ""
51-
WHERE provider = $1 AND \"userId\" = $2
52-
`
53-
try {
54-
await this.db.query(query, [this.provider, userId])
55-
} catch (error) {
56-
console.error(error)
57-
throw error
58-
}
59-
}
35+
async delete(_userId: string): Promise<void> {}
6036
}

src/features/auth/domain/oauth-token/repository/CompositeOAuthTokenRepository.ts

Lines changed: 0 additions & 33 deletions
This file was deleted.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { IOAuthTokenRepository, OAuthToken } from ".."
2+
3+
export default class FallbackOAuthTokenRepository implements IOAuthTokenRepository {
4+
private readonly primaryRepository: IOAuthTokenRepository
5+
private readonly secondaryRepository: IOAuthTokenRepository
6+
7+
constructor(config: {
8+
primaryRepository: IOAuthTokenRepository,
9+
secondaryRepository: IOAuthTokenRepository
10+
}) {
11+
this.primaryRepository = config.primaryRepository
12+
this.secondaryRepository = config.secondaryRepository
13+
}
14+
15+
async get(userId: string): Promise<OAuthToken> {
16+
try {
17+
return await this.primaryRepository.get(userId)
18+
} catch {
19+
// Reading from the primary repository failed so we'll try the secondary repository.
20+
// However, we don't know if the error is due to the OAuth token not existing in
21+
// the primary repository or some other error occurred.
22+
// We might consider changing get(_:) on IOAuthTokenRepository to return null in the
23+
// case a token doesn't exist rather than throwing an error.
24+
const oauthToken = await this.secondaryRepository.get(userId)
25+
await this.primaryRepository.set(userId, oauthToken)
26+
return oauthToken
27+
}
28+
}
29+
30+
async set(userId: string, token: OAuthToken): Promise<void> {
31+
await this.primaryRepository.set(userId, token)
32+
}
33+
34+
async delete(userId: string): Promise<void> {
35+
await this.primaryRepository.delete(userId)
36+
}
37+
}

0 commit comments

Comments
 (0)