Skip to content

Commit

Permalink
refactor(oauth-provider): internalize replay-store implementations
Browse files Browse the repository at this point in the history
  • Loading branch information
matthieusieben committed Apr 24, 2024
1 parent e1af30f commit 618fb69
Show file tree
Hide file tree
Showing 19 changed files with 39 additions and 145 deletions.
1 change: 0 additions & 1 deletion packages/oauth-provider-replay-memory/TODO.md

This file was deleted.

36 changes: 0 additions & 36 deletions packages/oauth-provider-replay-memory/package.json

This file was deleted.

1 change: 0 additions & 1 deletion packages/oauth-provider-replay-memory/src/index.ts

This file was deleted.

8 changes: 0 additions & 8 deletions packages/oauth-provider-replay-memory/tsconfig.json

This file was deleted.

1 change: 0 additions & 1 deletion packages/oauth-provider-replay-redis/TODO.md

This file was deleted.

37 changes: 0 additions & 37 deletions packages/oauth-provider-replay-redis/package.json

This file was deleted.

2 changes: 0 additions & 2 deletions packages/oauth-provider-replay-redis/src/index.ts

This file was deleted.

8 changes: 0 additions & 8 deletions packages/oauth-provider-replay-redis/tsconfig.json

This file was deleted.

2 changes: 2 additions & 0 deletions packages/oauth-provider/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
"@types/send": "^0.17.4",
"@web/rollup-plugin-import-meta-assets": "^2.2.1",
"autoprefixer": "^10.4.17",
"ioredis": "^5.3.2",
"postcss": "^8.4.33",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand All @@ -65,6 +66,7 @@
"typescript": "^5.3.3"
},
"optionalDependencies": {
"ioredis": "^5.3.2",
"keygrip": "^1.1.0"
},
"scripts": {
Expand Down
4 changes: 2 additions & 2 deletions packages/oauth-provider/src/oauth-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ import {
} from './output/send-authorize-redirect.js'
import { sendErrorPage } from './output/send-error-page.js'
import { oidcPayload } from './parameters/oidc-payload.js'
import { ReplayStore, asReplayStore } from './replay/replay-store.js'
import { ReplayStore, ifReplayStore } from './replay/replay-store.js'
import { RequestManager } from './request/request-manager.js'
import { RequestStoreMemory } from './request/request-store-memory.js'
import { RequestStore, isRequestStore } from './request/request-store.js'
Expand Down Expand Up @@ -185,7 +185,7 @@ export class OAuthProvider extends OAuthVerifier {

accountStore = asAccountStore(store),
clientStore = asClientStore(store),
replayStore = asReplayStore(store),
replayStore = ifReplayStore(store),
requestStore = store && isRequestStore(store)
? store
: new RequestStoreMemory(),
Expand Down
17 changes: 15 additions & 2 deletions packages/oauth-provider/src/oauth-verifier.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Keyset, jwtSchema } from '@atproto/jwk'
import { AccessToken, OAuthTokenType } from '@atproto/oauth-types'
import { Redis, type RedisOptions } from 'ioredis'

import { AccessTokenType } from './access-token/access-token-type.js'
import { DpopManager, DpopManagerOptions } from './dpop/dpop-manager.js'
import { DpopNonce } from './dpop/dpop-nonce.js'
Expand All @@ -16,6 +18,8 @@ import {
VerifyTokenClaimsResult,
verifyTokenClaims,
} from './token/verify-token-claims.js'
import { ReplayStoreRedis } from './replay/replay-store-redis.js'
import { ReplayStoreMemory } from './replay/replay-store-memory.js'

export type OAuthVerifierOptions = Override<
DpopManagerOptions,
Expand Down Expand Up @@ -50,7 +54,13 @@ export type OAuthVerifierOptions = Override<
*/
accessTokenType?: AccessTokenType

replayStore: ReplayStore
/**
* A redis instance to use for replay protection. If not provided, replay
* protection will use memory storage.
*/
redis?: Redis | RedisOptions | string

replayStore?: ReplayStore
}
>

Expand All @@ -72,9 +82,12 @@ export class OAuthVerifier {
protected readonly signer: Signer

constructor({
redis,
issuer,
keyset,
replayStore,
replayStore = redis != null
? new ReplayStoreRedis(redis)
: new ReplayStoreMemory(),
accessTokenType = AccessTokenType.jwt,

...dpopMgrOptions
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { ReplayStore } from '@atproto/oauth-provider'

export class OAuthReplayStoreMemory implements ReplayStore {
export class ReplayStoreMemory implements ReplayStore {
private lastCleanup = Date.now()
private nonces = new Map<string, number>()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import { Redis, type RedisOptions } from 'ioredis'

export type { RedisOptions, Redis }

export type OAuthReplayStoreRedisOptions = Redis | string | RedisOptions
export type ReplayStoreRedisOptions = Redis | RedisOptions | string

export class OAuthReplayStoreRedis implements ReplayStore {
export class ReplayStoreRedis implements ReplayStore {
private readonly redis: Redis

constructor(options: OAuthReplayStoreRedisOptions) {
constructor(options: ReplayStoreRedisOptions) {
if (options instanceof Redis) {
this.redis = options
} else if (typeof options === 'string') {
Expand Down
9 changes: 9 additions & 0 deletions packages/oauth-provider/src/replay/replay-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ export function isReplayStore(
return typeof implementation.unique === 'function'
}

export function ifReplayStore(
implementation?: Record<string, unknown> & Partial<ReplayStore>,
): ReplayStore | undefined {
if (!implementation || !isReplayStore(implementation)) {
return undefined
}
return implementation
}

export function asReplayStore(
implementation?: Record<string, unknown> & Partial<ReplayStore>,
): ReplayStore {
Expand Down
2 changes: 0 additions & 2 deletions packages/pds/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@
"@atproto/lexicon": "workspace:^",
"@atproto/oauth-provider": "workspace:^",
"@atproto/oauth-provider-client-uri": "workspace:^",
"@atproto/oauth-provider-replay-memory": "workspace:^",
"@atproto/oauth-provider-replay-redis": "workspace:^",
"@atproto/repo": "workspace:^",
"@atproto/syntax": "workspace:^",
"@atproto/xrpc": "workspace:^",
Expand Down
10 changes: 3 additions & 7 deletions packages/pds/src/auth-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,9 @@ import {
LoginCredentials,
OAuthProvider,
} from '@atproto/oauth-provider'
import { OAuthReplayStoreMemory } from '@atproto/oauth-provider-replay-memory'
import { OAuthReplayStoreRedis } from '@atproto/oauth-provider-replay-redis'
import { CachedGetter } from '@atproto/simple-store'
import { SimpleStoreMemory } from '@atproto/simple-store-memory'
import { Redis } from 'ioredis'
import { Redis, RedisOptions } from 'ioredis'

import { AccountManager } from './account-manager'
import { ActorStore } from './actor-store'
Expand All @@ -33,7 +31,7 @@ export type AuthProviderOptions = {
accountManager: AccountManager
actorStore: ActorStore
localViewer: LocalViewerCreator
redis?: Redis
redis?: Redis | RedisOptions | string
dpopSecret?: DpopManagerOptions['dpopSecret']
customization?: Customization
disableSsrf?: boolean
Expand Down Expand Up @@ -64,6 +62,7 @@ export class AuthProvider extends OAuthProvider {
issuer,
keyset,
dpopSecret,
redis,

// Even though the accountManager implements the AccountStore interface,
// the accounts it returns do not contain any profile information (display
Expand All @@ -78,9 +77,6 @@ export class AuthProvider extends OAuthProvider {
requestStore: accountManager,
deviceStore: accountManager,
tokenStore: accountManager,
replayStore: redis
? new OAuthReplayStoreRedis(redis)
: new OAuthReplayStoreMemory(),
clientStore: new OauthClientStore({
// A Fetch function that protects against SSRF attacks, large responses &
// known bad domains. This function can safely be used to fetch user
Expand Down
6 changes: 1 addition & 5 deletions packages/pds/src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import { JoseKeyset } from '@atproto/jwk-jose'
import { createServiceAuthHeaders } from '@atproto/xrpc-server'
import { BlobStore } from '@atproto/repo'
import { OAuthVerifier } from '@atproto/oauth-provider'
import { OAuthReplayStoreRedis } from '@atproto/oauth-provider-replay-redis'
import { OAuthReplayStoreMemory } from '@atproto/oauth-provider-replay-memory'

import { ServerConfig, ServerSecrets } from './config'
import { AuthProvider } from './auth-provider'
Expand Down Expand Up @@ -244,9 +242,7 @@ export class AppContext {
issuer: cfg.oauth.issuer,
keyset,
dpopSecret: secrets.dpopSecret,
replayStore: redisScratch
? new OAuthReplayStoreRedis(redisScratch)
: new OAuthReplayStoreMemory(),
redis: redisScratch,
})

const jwtKey = cfg.entryway
Expand Down
30 changes: 3 additions & 27 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@
{ "path": "./packages/oauth-client-react-native" },
{ "path": "./packages/oauth-provider" },
{ "path": "./packages/oauth-provider-client-uri" },
{ "path": "./packages/oauth-provider-replay-memory" },
{ "path": "./packages/oauth-provider-replay-redis" },
{ "path": "./packages/oauth-types" },
{ "path": "./packages/ozone" },
{ "path": "./packages/pds" },
Expand Down

0 comments on commit 618fb69

Please sign in to comment.