@atproto/pds 0.5.11 → 0.5.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +46 -0
- package/dist/api/app/bsky/notification/index.d.ts.map +1 -1
- package/dist/api/app/bsky/notification/index.js +2 -0
- package/dist/api/app/bsky/notification/index.js.map +1 -1
- package/dist/api/app/bsky/notification/unregisterPush.d.ts +4 -0
- package/dist/api/app/bsky/notification/unregisterPush.d.ts.map +1 -0
- package/dist/api/app/bsky/notification/unregisterPush.js +48 -0
- package/dist/api/app/bsky/notification/unregisterPush.js.map +1 -0
- package/package.json +42 -38
- package/build.templates.cjs +0 -22
- package/example.env +0 -42
- package/jest.config.cjs +0 -27
- package/src/account-manager/account-manager.ts +0 -916
- package/src/account-manager/db/index.ts +0 -21
- package/src/account-manager/db/migrations/001-init.ts +0 -115
- package/src/account-manager/db/migrations/002-account-deactivation.ts +0 -17
- package/src/account-manager/db/migrations/003-privileged-app-passwords.ts +0 -12
- package/src/account-manager/db/migrations/004-oauth.ts +0 -122
- package/src/account-manager/db/migrations/005-oauth-account-management.ts +0 -136
- package/src/account-manager/db/migrations/006-oauth-permission-sets.ts +0 -20
- package/src/account-manager/db/migrations/007-lexicon-failures-index.ts +0 -14
- package/src/account-manager/db/migrations/index.ts +0 -17
- package/src/account-manager/db/schema/account-device.ts +0 -14
- package/src/account-manager/db/schema/account.ts +0 -16
- package/src/account-manager/db/schema/actor.ts +0 -17
- package/src/account-manager/db/schema/app-password.ts +0 -13
- package/src/account-manager/db/schema/authorization-request.ts +0 -30
- package/src/account-manager/db/schema/authorized-client.ts +0 -23
- package/src/account-manager/db/schema/device.ts +0 -18
- package/src/account-manager/db/schema/email-token.ts +0 -19
- package/src/account-manager/db/schema/index.ts +0 -43
- package/src/account-manager/db/schema/invite-code.ts +0 -24
- package/src/account-manager/db/schema/lexicon.ts +0 -15
- package/src/account-manager/db/schema/refresh-token.ts +0 -11
- package/src/account-manager/db/schema/repo-root.ts +0 -12
- package/src/account-manager/db/schema/token.ts +0 -38
- package/src/account-manager/db/schema/used-refresh-token.ts +0 -13
- package/src/account-manager/helpers/account-device.ts +0 -63
- package/src/account-manager/helpers/account.ts +0 -355
- package/src/account-manager/helpers/auth.ts +0 -222
- package/src/account-manager/helpers/authorization-request.ts +0 -90
- package/src/account-manager/helpers/authorized-client.ts +0 -75
- package/src/account-manager/helpers/device.ts +0 -47
- package/src/account-manager/helpers/email-token.ts +0 -87
- package/src/account-manager/helpers/invite.ts +0 -263
- package/src/account-manager/helpers/lexicon.ts +0 -67
- package/src/account-manager/helpers/password.ts +0 -136
- package/src/account-manager/helpers/repo.ts +0 -24
- package/src/account-manager/helpers/scrypt.ts +0 -53
- package/src/account-manager/helpers/token.ts +0 -158
- package/src/account-manager/helpers/used-refresh-token.ts +0 -30
- package/src/account-manager/oauth-store.ts +0 -880
- package/src/account-manager/scope-reference-getter.ts +0 -106
- package/src/actor-store/actor-store-reader.ts +0 -45
- package/src/actor-store/actor-store-resources.ts +0 -8
- package/src/actor-store/actor-store-transactor.ts +0 -31
- package/src/actor-store/actor-store-writer.ts +0 -17
- package/src/actor-store/actor-store.ts +0 -210
- package/src/actor-store/blob/reader.ts +0 -160
- package/src/actor-store/blob/transactor.ts +0 -383
- package/src/actor-store/db/index.ts +0 -20
- package/src/actor-store/db/migrations/001-init.ts +0 -105
- package/src/actor-store/db/migrations/index.ts +0 -5
- package/src/actor-store/db/schema/account-pref.ts +0 -11
- package/src/actor-store/db/schema/backlink.ts +0 -9
- package/src/actor-store/db/schema/blob.ts +0 -14
- package/src/actor-store/db/schema/index.ts +0 -23
- package/src/actor-store/db/schema/record-blob.ts +0 -8
- package/src/actor-store/db/schema/record.ts +0 -14
- package/src/actor-store/db/schema/repo-block.ts +0 -10
- package/src/actor-store/db/schema/repo-root.ts +0 -10
- package/src/actor-store/migrate.ts +0 -39
- package/src/actor-store/preference/reader.ts +0 -48
- package/src/actor-store/preference/transactor.ts +0 -55
- package/src/actor-store/preference/util.ts +0 -39
- package/src/actor-store/record/reader.ts +0 -374
- package/src/actor-store/record/transactor.ts +0 -111
- package/src/actor-store/repo/reader.ts +0 -31
- package/src/actor-store/repo/sql-repo-reader.ts +0 -150
- package/src/actor-store/repo/sql-repo-transactor.ts +0 -105
- package/src/actor-store/repo/transactor.ts +0 -227
- package/src/api/app/bsky/actor/getPreferences.ts +0 -57
- package/src/api/app/bsky/actor/getProfile.ts +0 -41
- package/src/api/app/bsky/actor/getProfiles.ts +0 -51
- package/src/api/app/bsky/actor/index.ts +0 -13
- package/src/api/app/bsky/actor/putPreferences.ts +0 -62
- package/src/api/app/bsky/feed/getActorLikes.ts +0 -63
- package/src/api/app/bsky/feed/getAuthorFeed.ts +0 -84
- package/src/api/app/bsky/feed/getFeed.ts +0 -47
- package/src/api/app/bsky/feed/getPostThread.ts +0 -251
- package/src/api/app/bsky/feed/getTimeline.ts +0 -50
- package/src/api/app/bsky/feed/index.ts +0 -15
- package/src/api/app/bsky/index.ts +0 -11
- package/src/api/app/bsky/notification/index.ts +0 -7
- package/src/api/app/bsky/notification/registerPush.ts +0 -71
- package/src/api/app/bsky/util/resolver.ts +0 -20
- package/src/api/com/atproto/admin/deleteAccount.ts +0 -22
- package/src/api/com/atproto/admin/disableAccountInvites.ts +0 -18
- package/src/api/com/atproto/admin/disableInviteCodes.ts +0 -21
- package/src/api/com/atproto/admin/enableAccountInvites.ts +0 -18
- package/src/api/com/atproto/admin/getAccountInfo.ts +0 -32
- package/src/api/com/atproto/admin/getAccountInfos.ts +0 -34
- package/src/api/com/atproto/admin/getInviteCodes.ts +0 -126
- package/src/api/com/atproto/admin/getSubjectStatus.ts +0 -76
- package/src/api/com/atproto/admin/index.ts +0 -31
- package/src/api/com/atproto/admin/sendEmail.ts +0 -47
- package/src/api/com/atproto/admin/updateAccountEmail.ts +0 -32
- package/src/api/com/atproto/admin/updateAccountHandle.ts +0 -47
- package/src/api/com/atproto/admin/updateAccountPassword.ts +0 -34
- package/src/api/com/atproto/admin/updateSubjectStatus.ts +0 -68
- package/src/api/com/atproto/admin/util.ts +0 -66
- package/src/api/com/atproto/identity/getRecommendedDidCredentials.ts +0 -50
- package/src/api/com/atproto/identity/index.ts +0 -17
- package/src/api/com/atproto/identity/requestPlcOperationSignature.ts +0 -59
- package/src/api/com/atproto/identity/resolveHandle.ts +0 -50
- package/src/api/com/atproto/identity/signPlcOperation.ts +0 -85
- package/src/api/com/atproto/identity/submitPlcOperation.ts +0 -65
- package/src/api/com/atproto/identity/updateHandle.ts +0 -66
- package/src/api/com/atproto/index.ts +0 -19
- package/src/api/com/atproto/moderation/createReport.ts +0 -41
- package/src/api/com/atproto/moderation/index.ts +0 -7
- package/src/api/com/atproto/repo/applyWrites.ts +0 -226
- package/src/api/com/atproto/repo/createRecord.ts +0 -143
- package/src/api/com/atproto/repo/deleteRecord.ts +0 -122
- package/src/api/com/atproto/repo/describeRepo.ts +0 -43
- package/src/api/com/atproto/repo/getRecord.ts +0 -40
- package/src/api/com/atproto/repo/importRepo.ts +0 -101
- package/src/api/com/atproto/repo/index.ts +0 -25
- package/src/api/com/atproto/repo/listMissingBlobs.ts +0 -29
- package/src/api/com/atproto/repo/listRecords.ts +0 -36
- package/src/api/com/atproto/repo/putRecord.ts +0 -211
- package/src/api/com/atproto/repo/uploadBlob.ts +0 -60
- package/src/api/com/atproto/server/activateAccount.ts +0 -37
- package/src/api/com/atproto/server/checkAccountStatus.ts +0 -51
- package/src/api/com/atproto/server/confirmEmail.ts +0 -39
- package/src/api/com/atproto/server/createAccount.ts +0 -327
- package/src/api/com/atproto/server/createAppPassword.ts +0 -53
- package/src/api/com/atproto/server/createInviteCode.ts +0 -38
- package/src/api/com/atproto/server/createInviteCodes.ts +0 -42
- package/src/api/com/atproto/server/createSession.ts +0 -97
- package/src/api/com/atproto/server/deactivateAccount.ts +0 -41
- package/src/api/com/atproto/server/deleteAccount.ts +0 -85
- package/src/api/com/atproto/server/deleteSession.ts +0 -23
- package/src/api/com/atproto/server/describeServer.ts +0 -29
- package/src/api/com/atproto/server/getAccountInviteCodes.ts +0 -142
- package/src/api/com/atproto/server/getServiceAuth.ts +0 -111
- package/src/api/com/atproto/server/getSession.ts +0 -80
- package/src/api/com/atproto/server/index.ts +0 -55
- package/src/api/com/atproto/server/listAppPasswords.ts +0 -45
- package/src/api/com/atproto/server/refreshSession.ts +0 -72
- package/src/api/com/atproto/server/requestAccountDelete.ts +0 -66
- package/src/api/com/atproto/server/requestEmailConfirmation.ts +0 -68
- package/src/api/com/atproto/server/requestEmailUpdate.ts +0 -86
- package/src/api/com/atproto/server/requestPasswordReset.ts +0 -52
- package/src/api/com/atproto/server/reserveSigningKey.ts +0 -15
- package/src/api/com/atproto/server/resetPassword.ts +0 -50
- package/src/api/com/atproto/server/revokeAppPassword.ts +0 -42
- package/src/api/com/atproto/server/updateEmail.ts +0 -52
- package/src/api/com/atproto/server/util.ts +0 -136
- package/src/api/com/atproto/sync/deprecated/getCheckout.ts +0 -27
- package/src/api/com/atproto/sync/deprecated/getHead.ts +0 -33
- package/src/api/com/atproto/sync/getBlob.ts +0 -61
- package/src/api/com/atproto/sync/getBlocks.ts +0 -37
- package/src/api/com/atproto/sync/getLatestCommit.ts +0 -33
- package/src/api/com/atproto/sync/getRecord.ts +0 -49
- package/src/api/com/atproto/sync/getRepo.ts +0 -75
- package/src/api/com/atproto/sync/getRepoStatus.ts +0 -34
- package/src/api/com/atproto/sync/index.ts +0 -27
- package/src/api/com/atproto/sync/listBlobs.ts +0 -33
- package/src/api/com/atproto/sync/listRepos.ts +0 -74
- package/src/api/com/atproto/sync/subscribeRepos.ts +0 -73
- package/src/api/com/atproto/sync/util.ts +0 -37
- package/src/api/com/atproto/temp/checkSignupQueue.ts +0 -34
- package/src/api/com/atproto/temp/index.ts +0 -7
- package/src/api/index.ts +0 -10
- package/src/api/proxy.ts +0 -44
- package/src/app-view.ts +0 -24
- package/src/auth-output.ts +0 -52
- package/src/auth-routes.ts +0 -64
- package/src/auth-scope.ts +0 -40
- package/src/auth-verifier.ts +0 -668
- package/src/background.ts +0 -65
- package/src/basic-routes.ts +0 -52
- package/src/bsky-app-view.ts +0 -33
- package/src/config/config.ts +0 -553
- package/src/config/env.ts +0 -167
- package/src/config/index.ts +0 -3
- package/src/config/secrets.ts +0 -54
- package/src/context.ts +0 -523
- package/src/crawlers.ts +0 -46
- package/src/db/cast.ts +0 -52
- package/src/db/db.ts +0 -140
- package/src/db/index.ts +0 -4
- package/src/db/migrator.ts +0 -40
- package/src/db/pagination.ts +0 -132
- package/src/db/tables/moderation.ts +0 -80
- package/src/db/util.ts +0 -108
- package/src/did-cache/db/index.ts +0 -21
- package/src/did-cache/db/migrations.ts +0 -17
- package/src/did-cache/db/schema.ts +0 -9
- package/src/did-cache/index.ts +0 -131
- package/src/disk-blobstore.ts +0 -172
- package/src/error.ts +0 -19
- package/src/handle/explicit-slurs.ts +0 -22
- package/src/handle/index.ts +0 -49
- package/src/handle/reserved.ts +0 -1059
- package/src/image/image-url-builder.ts +0 -16
- package/src/index.ts +0 -189
- package/src/lexicons.ts +0 -4
- package/src/logger.ts +0 -35
- package/src/mailer/index.ts +0 -111
- package/src/mailer/moderation.ts +0 -33
- package/src/mailer/templates/confirm-email.d.ts +0 -4
- package/src/mailer/templates/confirm-email.hbs +0 -158
- package/src/mailer/templates/delete-account.d.ts +0 -4
- package/src/mailer/templates/delete-account.hbs +0 -156
- package/src/mailer/templates/plc-operation.d.ts +0 -4
- package/src/mailer/templates/plc-operation.hbs +0 -153
- package/src/mailer/templates/reset-password.d.ts +0 -4
- package/src/mailer/templates/reset-password.hbs +0 -154
- package/src/mailer/templates/update-email.d.ts +0 -4
- package/src/mailer/templates/update-email.hbs +0 -153
- package/src/mailer/templates.ts +0 -17
- package/src/pipethrough.ts +0 -716
- package/src/rate-limits.ts +0 -59
- package/src/read-after-write/index.ts +0 -3
- package/src/read-after-write/types.ts +0 -35
- package/src/read-after-write/util.ts +0 -101
- package/src/read-after-write/viewer.ts +0 -290
- package/src/redis.ts +0 -25
- package/src/repo/index.ts +0 -2
- package/src/repo/prepare.ts +0 -266
- package/src/repo/types.ts +0 -65
- package/src/scripts/README.md +0 -40
- package/src/scripts/index.ts +0 -20
- package/src/scripts/publish-identity.ts +0 -60
- package/src/scripts/rebuild-repo.ts +0 -119
- package/src/scripts/rotate-keys.ts +0 -146
- package/src/scripts/sequencer-recovery/index.ts +0 -23
- package/src/scripts/sequencer-recovery/recoverer.ts +0 -297
- package/src/scripts/sequencer-recovery/recovery-db.ts +0 -65
- package/src/scripts/sequencer-recovery/repair-repos.ts +0 -49
- package/src/scripts/sequencer-recovery/user-queues.ts +0 -44
- package/src/scripts/util.ts +0 -7
- package/src/sequencer/db/index.ts +0 -21
- package/src/sequencer/db/migrations/001-init.ts +0 -35
- package/src/sequencer/db/migrations/index.ts +0 -5
- package/src/sequencer/db/schema.ts +0 -19
- package/src/sequencer/events.ts +0 -199
- package/src/sequencer/index.ts +0 -3
- package/src/sequencer/outbox.ts +0 -123
- package/src/sequencer/sequencer.ts +0 -290
- package/src/util/compression.ts +0 -16
- package/src/util/debug.ts +0 -10
- package/src/util/http.ts +0 -31
- package/src/util/types.ts +0 -7
- package/src/well-known.ts +0 -30
- package/test.env +0 -2
- package/tests/__snapshots__/takedown-appeal.test.ts.snap +0 -43
- package/tests/_oauth_client_assets_middleware.ts +0 -23
- package/tests/_puppeteer.ts +0 -147
- package/tests/_types.d.ts +0 -16
- package/tests/_util.ts +0 -191
- package/tests/account-deactivation.test.ts +0 -204
- package/tests/account-deletion.test.ts +0 -300
- package/tests/account-manager.test.ts +0 -462
- package/tests/account-migration.test.ts +0 -221
- package/tests/account-status.test.ts +0 -206
- package/tests/account.test.ts +0 -595
- package/tests/app-passwords.test.ts +0 -262
- package/tests/auth.test.ts +0 -405
- package/tests/blob-deletes.test.ts +0 -204
- package/tests/create-post.test.ts +0 -79
- package/tests/crud.test.ts +0 -1595
- package/tests/db.test.ts +0 -170
- package/tests/email-confirmation.test.ts +0 -227
- package/tests/entryway-mock.ts +0 -323
- package/tests/entryway.test.ts +0 -145
- package/tests/file-uploads.test.ts +0 -301
- package/tests/get-service-auth.test.ts +0 -81
- package/tests/handle-validation.test.ts +0 -56
- package/tests/handles.test.ts +0 -274
- package/tests/invite-codes.test.ts +0 -367
- package/tests/invites-admin.test.ts +0 -252
- package/tests/moderation.test.ts +0 -280
- package/tests/moderator-auth.test.ts +0 -177
- package/tests/oauth.test.ts +0 -334
- package/tests/plc-operations.test.ts +0 -239
- package/tests/preferences.test.ts +0 -352
- package/tests/proxied/__snapshots__/admin.test.ts.snap +0 -516
- package/tests/proxied/__snapshots__/feedgen.test.ts.snap +0 -215
- package/tests/proxied/__snapshots__/views.test.ts.snap +0 -4456
- package/tests/proxied/admin.test.ts +0 -340
- package/tests/proxied/feedgen.test.ts +0 -66
- package/tests/proxied/notif.test.ts +0 -85
- package/tests/proxied/procedures.test.ts +0 -175
- package/tests/proxied/proxy-catchall.test.ts +0 -256
- package/tests/proxied/proxy-header.test.ts +0 -239
- package/tests/proxied/proxy-oauth-aud.test.ts +0 -182
- package/tests/proxied/read-after-write.test.ts +0 -382
- package/tests/proxied/views.test.ts +0 -608
- package/tests/races.test.ts +0 -89
- package/tests/rate-limits.test.ts +0 -70
- package/tests/recovery.test.ts +0 -180
- package/tests/seeds/basic.ts +0 -165
- package/tests/seeds/follows.ts +0 -57
- package/tests/seeds/likes.ts +0 -14
- package/tests/seeds/reposts.ts +0 -18
- package/tests/seeds/thread.ts +0 -40
- package/tests/seeds/users-bulk.ts +0 -246
- package/tests/seeds/users.ts +0 -67
- package/tests/sequencer.test.ts +0 -251
- package/tests/server.test.ts +0 -151
- package/tests/sync/invertible-ops.test.ts +0 -99
- package/tests/sync/list.test.ts +0 -43
- package/tests/sync/subscribe-repos.test.ts +0 -672
- package/tests/sync/sync.test.ts +0 -316
- package/tests/takedown-appeal.test.ts +0 -166
- package/tsconfig.build.json +0 -9
- package/tsconfig.build.tsbuildinfo +0 -1
- package/tsconfig.json +0 -7
- package/tsconfig.tests.json +0 -8
package/src/auth-scope.ts
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
// @TODO sync-up with current method names, consider backwards compat.
|
|
2
|
-
export enum AuthScope {
|
|
3
|
-
Access = 'com.atproto.access',
|
|
4
|
-
Refresh = 'com.atproto.refresh',
|
|
5
|
-
AppPass = 'com.atproto.appPass',
|
|
6
|
-
AppPassPrivileged = 'com.atproto.appPassPrivileged',
|
|
7
|
-
SignupQueued = 'com.atproto.signupQueued',
|
|
8
|
-
Takendown = 'com.atproto.takendown',
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export const ACCESS_FULL = [AuthScope.Access] as const
|
|
12
|
-
export const ACCESS_PRIVILEGED = [
|
|
13
|
-
...ACCESS_FULL,
|
|
14
|
-
AuthScope.AppPassPrivileged,
|
|
15
|
-
] as const
|
|
16
|
-
export const ACCESS_STANDARD = [
|
|
17
|
-
...ACCESS_PRIVILEGED,
|
|
18
|
-
AuthScope.AppPass,
|
|
19
|
-
] as const
|
|
20
|
-
|
|
21
|
-
const authScopesValues = new Set(Object.values(AuthScope))
|
|
22
|
-
export function isAuthScope(val: unknown): val is AuthScope {
|
|
23
|
-
return (authScopesValues as Set<unknown>).has(val)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function isAccessFull(
|
|
27
|
-
scope: AuthScope,
|
|
28
|
-
): scope is (typeof ACCESS_FULL)[number] {
|
|
29
|
-
return (ACCESS_FULL as readonly string[]).includes(scope)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function isAccessPrivileged(
|
|
33
|
-
scope: AuthScope,
|
|
34
|
-
): scope is (typeof ACCESS_PRIVILEGED)[number] {
|
|
35
|
-
return (ACCESS_PRIVILEGED as readonly string[]).includes(scope)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function isTakendown(scope: unknown): scope is AuthScope.Takendown {
|
|
39
|
-
return scope === AuthScope.Takendown
|
|
40
|
-
}
|
package/src/auth-verifier.ts
DELETED
|
@@ -1,668 +0,0 @@
|
|
|
1
|
-
import { KeyObject, createPublicKey, createSecretKey } from 'node:crypto'
|
|
2
|
-
import { IncomingMessage, ServerResponse } from 'node:http'
|
|
3
|
-
import * as jose from 'jose'
|
|
4
|
-
import KeyEncoderModule from 'key-encoder'
|
|
5
|
-
import { getVerificationMaterial } from '@atproto/common'
|
|
6
|
-
import { IdResolver, getDidKeyFromMultibase } from '@atproto/identity'
|
|
7
|
-
import { AtIdentifierString, DidString, isDidString } from '@atproto/lex'
|
|
8
|
-
import {
|
|
9
|
-
OAuthError,
|
|
10
|
-
OAuthVerifier,
|
|
11
|
-
VerifyTokenPayloadOptions,
|
|
12
|
-
WWWAuthenticateError,
|
|
13
|
-
} from '@atproto/oauth-provider'
|
|
14
|
-
import {
|
|
15
|
-
ScopePermissions,
|
|
16
|
-
ScopePermissionsTransition,
|
|
17
|
-
} from '@atproto/oauth-scopes'
|
|
18
|
-
import {
|
|
19
|
-
AuthRequiredError,
|
|
20
|
-
Awaitable,
|
|
21
|
-
ForbiddenError,
|
|
22
|
-
InvalidRequestError,
|
|
23
|
-
MethodAuthContext,
|
|
24
|
-
MethodAuthVerifier,
|
|
25
|
-
Params,
|
|
26
|
-
XRPCError,
|
|
27
|
-
parseReqNsid,
|
|
28
|
-
verifyJwt as verifyServiceJwt,
|
|
29
|
-
} from '@atproto/xrpc-server'
|
|
30
|
-
import { AccountManager } from './account-manager/account-manager.js'
|
|
31
|
-
import { ActorAccount } from './account-manager/helpers/account.js'
|
|
32
|
-
import {
|
|
33
|
-
AccessOutput,
|
|
34
|
-
AdminTokenOutput,
|
|
35
|
-
ModServiceOutput,
|
|
36
|
-
OAuthOutput,
|
|
37
|
-
RefreshOutput,
|
|
38
|
-
UnauthenticatedOutput,
|
|
39
|
-
UserServiceAuthOutput,
|
|
40
|
-
} from './auth-output.js'
|
|
41
|
-
import { ACCESS_STANDARD, AuthScope, isAuthScope } from './auth-scope.js'
|
|
42
|
-
import { softDeleted } from './db/index.js'
|
|
43
|
-
import { appendVary } from './util/http.js'
|
|
44
|
-
import { WithRequired } from './util/types.js'
|
|
45
|
-
|
|
46
|
-
// key-encoder is CJS with exports.default; Node ESM interop wraps it as { default: Class }
|
|
47
|
-
const KeyEncoder = ((m) => m.default ?? m)(KeyEncoderModule)
|
|
48
|
-
|
|
49
|
-
export type VerifiedOptions = {
|
|
50
|
-
checkTakedown?: boolean
|
|
51
|
-
checkDeactivated?: boolean
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export type ScopedOptions<S extends AuthScope = AuthScope> = {
|
|
55
|
-
scopes?: readonly S[]
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export type ExtraScopedOptions<S extends AuthScope = AuthScope> = {
|
|
59
|
-
additional?: readonly S[]
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export type AuthorizedOptions<P extends Params = Params> = {
|
|
63
|
-
authorize: (
|
|
64
|
-
permissions: ScopePermissions,
|
|
65
|
-
ctx: MethodAuthContext<P>,
|
|
66
|
-
) => Awaitable<void>
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export type AuthVerifierOpts = {
|
|
70
|
-
publicUrl: string
|
|
71
|
-
jwtKey: KeyObject
|
|
72
|
-
adminPass: string
|
|
73
|
-
dids: {
|
|
74
|
-
pds: string
|
|
75
|
-
entryway?: string
|
|
76
|
-
modService?: string
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export type VerifyBearerJwtOptions<S extends AuthScope = AuthScope> =
|
|
81
|
-
WithRequired<
|
|
82
|
-
Omit<jose.JWTVerifyOptions, 'scopes'> & {
|
|
83
|
-
scopes: readonly S[]
|
|
84
|
-
},
|
|
85
|
-
'audience' | 'typ'
|
|
86
|
-
>
|
|
87
|
-
|
|
88
|
-
export type VerifyBearerJwtResult<S extends AuthScope = AuthScope> = {
|
|
89
|
-
sub: DidString
|
|
90
|
-
aud: string
|
|
91
|
-
jti: string | undefined
|
|
92
|
-
scope: S
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export class AuthVerifier {
|
|
96
|
-
private _publicUrl: string
|
|
97
|
-
private _jwtKey: KeyObject
|
|
98
|
-
private _adminPass: string
|
|
99
|
-
public dids: AuthVerifierOpts['dids']
|
|
100
|
-
|
|
101
|
-
constructor(
|
|
102
|
-
public accountManager: AccountManager,
|
|
103
|
-
public idResolver: IdResolver,
|
|
104
|
-
public oauthVerifier: OAuthVerifier,
|
|
105
|
-
opts: AuthVerifierOpts,
|
|
106
|
-
) {
|
|
107
|
-
this._publicUrl = opts.publicUrl
|
|
108
|
-
this._jwtKey = opts.jwtKey
|
|
109
|
-
this._adminPass = opts.adminPass
|
|
110
|
-
this.dids = opts.dids
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// verifiers (arrow fns to preserve scope)
|
|
114
|
-
|
|
115
|
-
public unauthenticated: MethodAuthVerifier<UnauthenticatedOutput> = (ctx) => {
|
|
116
|
-
setAuthHeaders(ctx.res)
|
|
117
|
-
|
|
118
|
-
// @NOTE this auth method is typically used as fallback when no other auth
|
|
119
|
-
// method is applicable. This means that the presence of an "authorization"
|
|
120
|
-
// header means that that header is invalid (as it did not match any of the
|
|
121
|
-
// other auth methods).
|
|
122
|
-
if (ctx.req.headers['authorization']) {
|
|
123
|
-
throw new AuthRequiredError('Invalid authorization header')
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return {
|
|
127
|
-
credentials: null,
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
public adminToken: MethodAuthVerifier<AdminTokenOutput> = async (ctx) => {
|
|
132
|
-
setAuthHeaders(ctx.res)
|
|
133
|
-
const parsed = parseBasicAuth(ctx.req)
|
|
134
|
-
if (!parsed) {
|
|
135
|
-
throw new AuthRequiredError()
|
|
136
|
-
}
|
|
137
|
-
const { username, password } = parsed
|
|
138
|
-
if (username !== 'admin' || password !== this._adminPass) {
|
|
139
|
-
throw new AuthRequiredError()
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
return { credentials: { type: 'admin_token' } }
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
public modService: MethodAuthVerifier<ModServiceOutput> = async (ctx) => {
|
|
146
|
-
setAuthHeaders(ctx.res)
|
|
147
|
-
if (!this.dids.modService) {
|
|
148
|
-
throw new AuthRequiredError('Untrusted issuer', 'UntrustedIss')
|
|
149
|
-
}
|
|
150
|
-
const payload = await this.verifyServiceJwt(ctx.req, {
|
|
151
|
-
iss: [this.dids.modService, `${this.dids.modService}#atproto_labeler`],
|
|
152
|
-
})
|
|
153
|
-
return {
|
|
154
|
-
credentials: {
|
|
155
|
-
type: 'mod_service',
|
|
156
|
-
did: payload.iss,
|
|
157
|
-
},
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
public moderator: MethodAuthVerifier<AdminTokenOutput | ModServiceOutput> =
|
|
162
|
-
async (ctx) => {
|
|
163
|
-
const type = extractAuthType(ctx.req)
|
|
164
|
-
if (type === AuthType.BEARER) {
|
|
165
|
-
return this.modService(ctx)
|
|
166
|
-
} else {
|
|
167
|
-
return this.adminToken(ctx)
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
protected access<S extends AuthScope>(
|
|
172
|
-
options: VerifiedOptions & Required<ScopedOptions<S>>,
|
|
173
|
-
): MethodAuthVerifier<AccessOutput<S>> {
|
|
174
|
-
const { scopes, ...statusOptions } = options
|
|
175
|
-
|
|
176
|
-
const verifyJwtOptions: VerifyBearerJwtOptions<S> = {
|
|
177
|
-
audience: this.dids.pds,
|
|
178
|
-
typ: 'at+jwt',
|
|
179
|
-
scopes:
|
|
180
|
-
// @NOTE We can reject taken down credentials based on the scope if
|
|
181
|
-
// "checkTakedown" is set.
|
|
182
|
-
statusOptions.checkTakedown && scopes.includes(AuthScope.Takendown as S)
|
|
183
|
-
? scopes.filter((s) => s !== AuthScope.Takendown)
|
|
184
|
-
: scopes,
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
return async (ctx) => {
|
|
188
|
-
setAuthHeaders(ctx.res)
|
|
189
|
-
|
|
190
|
-
const { sub: did, scope } = await this.verifyBearerJwt(
|
|
191
|
-
ctx.req,
|
|
192
|
-
verifyJwtOptions,
|
|
193
|
-
)
|
|
194
|
-
|
|
195
|
-
await this.verifyStatus(did, statusOptions)
|
|
196
|
-
|
|
197
|
-
return {
|
|
198
|
-
credentials: { type: 'access', did, scope },
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
public refresh(options?: {
|
|
204
|
-
allowExpired?: boolean
|
|
205
|
-
}): MethodAuthVerifier<RefreshOutput> {
|
|
206
|
-
const verifyOptions: VerifyBearerJwtOptions<AuthScope.Refresh> = {
|
|
207
|
-
clockTolerance: options?.allowExpired ? Infinity : undefined,
|
|
208
|
-
typ: 'refresh+jwt',
|
|
209
|
-
// when using entryway, proxying refresh credentials
|
|
210
|
-
audience: this.dids.entryway ? this.dids.entryway : this.dids.pds,
|
|
211
|
-
scopes: [AuthScope.Refresh],
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
return async (ctx) => {
|
|
215
|
-
setAuthHeaders(ctx.res)
|
|
216
|
-
|
|
217
|
-
const result = await this.verifyBearerJwt(ctx.req, verifyOptions)
|
|
218
|
-
|
|
219
|
-
const tokenId = result.jti
|
|
220
|
-
if (!tokenId) {
|
|
221
|
-
throw new AuthRequiredError(
|
|
222
|
-
'Unexpected missing refresh token id',
|
|
223
|
-
'MissingTokenId',
|
|
224
|
-
)
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
return {
|
|
228
|
-
credentials: {
|
|
229
|
-
type: 'refresh',
|
|
230
|
-
did: result.sub,
|
|
231
|
-
scope: result.scope,
|
|
232
|
-
tokenId,
|
|
233
|
-
},
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
public authorization<P extends Params>({
|
|
239
|
-
scopes = ACCESS_STANDARD,
|
|
240
|
-
additional = [],
|
|
241
|
-
...options
|
|
242
|
-
}: VerifiedOptions &
|
|
243
|
-
ScopedOptions &
|
|
244
|
-
ExtraScopedOptions &
|
|
245
|
-
AuthorizedOptions<P>): MethodAuthVerifier<AccessOutput | OAuthOutput, P> {
|
|
246
|
-
const access = this.access({
|
|
247
|
-
...options,
|
|
248
|
-
scopes: [...scopes, ...additional],
|
|
249
|
-
})
|
|
250
|
-
const oauth = this.oauth(options)
|
|
251
|
-
|
|
252
|
-
return async (ctx) => {
|
|
253
|
-
const type = extractAuthType(ctx.req)
|
|
254
|
-
|
|
255
|
-
if (type === AuthType.BEARER) {
|
|
256
|
-
return access(ctx)
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
if (type === AuthType.DPOP) {
|
|
260
|
-
return oauth(ctx)
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
// Auth headers are set through the access and oauth methods so we only
|
|
264
|
-
// need to set them here if we reach this point
|
|
265
|
-
setAuthHeaders(ctx.res)
|
|
266
|
-
|
|
267
|
-
if (type !== null) {
|
|
268
|
-
throw new InvalidRequestError(
|
|
269
|
-
'Unexpected authorization type',
|
|
270
|
-
'InvalidToken',
|
|
271
|
-
)
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
throw new AuthRequiredError(undefined, 'AuthMissing')
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
public authorizationOrAdminTokenOptional<P extends Params>(
|
|
279
|
-
opts: VerifiedOptions & ExtraScopedOptions & AuthorizedOptions<P>,
|
|
280
|
-
): MethodAuthVerifier<
|
|
281
|
-
OAuthOutput | AccessOutput | AdminTokenOutput | UnauthenticatedOutput,
|
|
282
|
-
P
|
|
283
|
-
> {
|
|
284
|
-
const authorization = this.authorization(opts)
|
|
285
|
-
return async (ctx) => {
|
|
286
|
-
const type = extractAuthType(ctx.req)
|
|
287
|
-
if (type === AuthType.BEARER || type === AuthType.DPOP) {
|
|
288
|
-
return authorization(ctx)
|
|
289
|
-
} else if (type === AuthType.BASIC) {
|
|
290
|
-
return this.adminToken(ctx)
|
|
291
|
-
} else {
|
|
292
|
-
return this.unauthenticated(ctx)
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
public userServiceAuth: MethodAuthVerifier<UserServiceAuthOutput> = async (
|
|
298
|
-
ctx,
|
|
299
|
-
) => {
|
|
300
|
-
setAuthHeaders(ctx.res)
|
|
301
|
-
const payload = await this.verifyServiceJwt(ctx.req)
|
|
302
|
-
return {
|
|
303
|
-
credentials: {
|
|
304
|
-
type: 'user_service_auth',
|
|
305
|
-
did: payload.iss,
|
|
306
|
-
},
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
public userServiceAuthOptional: MethodAuthVerifier<
|
|
311
|
-
UserServiceAuthOutput | UnauthenticatedOutput
|
|
312
|
-
> = async (ctx) => {
|
|
313
|
-
const type = extractAuthType(ctx.req)
|
|
314
|
-
if (type === AuthType.BEARER) {
|
|
315
|
-
return await this.userServiceAuth(ctx)
|
|
316
|
-
} else {
|
|
317
|
-
return this.unauthenticated(ctx)
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
public authorizationOrUserServiceAuth<P extends Params>(
|
|
322
|
-
options: VerifiedOptions &
|
|
323
|
-
ScopedOptions &
|
|
324
|
-
ExtraScopedOptions &
|
|
325
|
-
AuthorizedOptions<P>,
|
|
326
|
-
): MethodAuthVerifier<UserServiceAuthOutput | OAuthOutput | AccessOutput, P> {
|
|
327
|
-
const authorizationVerifier = this.authorization(options)
|
|
328
|
-
return async (ctx) => {
|
|
329
|
-
if (isDefinitelyServiceAuth(ctx.req)) {
|
|
330
|
-
return this.userServiceAuth(ctx)
|
|
331
|
-
} else {
|
|
332
|
-
return authorizationVerifier(ctx)
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
protected oauth<P extends Params>({
|
|
338
|
-
authorize,
|
|
339
|
-
...verifyStatusOptions
|
|
340
|
-
}: VerifiedOptions & AuthorizedOptions<P>): MethodAuthVerifier<
|
|
341
|
-
OAuthOutput,
|
|
342
|
-
P
|
|
343
|
-
> {
|
|
344
|
-
const verifyTokenOptions: VerifyTokenPayloadOptions = {
|
|
345
|
-
audience: [this.dids.pds],
|
|
346
|
-
scope: ['atproto'],
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
return async (ctx) => {
|
|
350
|
-
setAuthHeaders(ctx.res)
|
|
351
|
-
|
|
352
|
-
const { req, res } = ctx
|
|
353
|
-
|
|
354
|
-
// https://datatracker.ietf.org/doc/html/rfc9449#section-8.2
|
|
355
|
-
const dpopNonce = this.oauthVerifier.nextDpopNonce()
|
|
356
|
-
if (dpopNonce) {
|
|
357
|
-
res.setHeader('DPoP-Nonce', dpopNonce)
|
|
358
|
-
res.appendHeader('Access-Control-Expose-Headers', 'DPoP-Nonce')
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
const originalUrl = req.originalUrl || req.url || '/'
|
|
362
|
-
const url = new URL(originalUrl, this._publicUrl)
|
|
363
|
-
|
|
364
|
-
const { scope, sub: did } = await this.oauthVerifier
|
|
365
|
-
.authenticateRequest(
|
|
366
|
-
req.method || 'GET',
|
|
367
|
-
url,
|
|
368
|
-
req.headers,
|
|
369
|
-
verifyTokenOptions,
|
|
370
|
-
)
|
|
371
|
-
.catch((err) => {
|
|
372
|
-
// Make sure to include any WWW-Authenticate header in the response
|
|
373
|
-
// (particularly useful for DPoP's "use_dpop_nonce" error)
|
|
374
|
-
if (err instanceof WWWAuthenticateError) {
|
|
375
|
-
res.setHeader('WWW-Authenticate', err.wwwAuthenticateHeader)
|
|
376
|
-
res.appendHeader(
|
|
377
|
-
'Access-Control-Expose-Headers',
|
|
378
|
-
'WWW-Authenticate',
|
|
379
|
-
)
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
if (err instanceof OAuthError) {
|
|
383
|
-
throw new XRPCError(err.status, err.error_description, err.error)
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
throw err
|
|
387
|
-
})
|
|
388
|
-
|
|
389
|
-
if (!isDidString(did)) {
|
|
390
|
-
throw new InvalidRequestError('Malformed token', 'InvalidToken')
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
await this.verifyStatus(did, verifyStatusOptions)
|
|
394
|
-
|
|
395
|
-
const permissions = new ScopePermissionsTransition(scope?.split(' '))
|
|
396
|
-
|
|
397
|
-
// Should never happen
|
|
398
|
-
if (!permissions.scopes.has('atproto')) {
|
|
399
|
-
throw new InvalidRequestError(
|
|
400
|
-
'OAuth token does not have "atproto" scope',
|
|
401
|
-
'InvalidToken',
|
|
402
|
-
)
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
await authorize(permissions, ctx)
|
|
406
|
-
|
|
407
|
-
return {
|
|
408
|
-
credentials: {
|
|
409
|
-
type: 'oauth',
|
|
410
|
-
did,
|
|
411
|
-
permissions,
|
|
412
|
-
},
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
protected async verifyStatus(
|
|
418
|
-
did: DidString,
|
|
419
|
-
options: VerifiedOptions,
|
|
420
|
-
): Promise<void> {
|
|
421
|
-
if (options.checkDeactivated || options.checkTakedown) {
|
|
422
|
-
await this.findAccount(did, options)
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
/**
|
|
427
|
-
* Finds an account by its handle or DID, returning possibly deactivated or
|
|
428
|
-
* taken down accounts (unless `options.checkDeactivated` or
|
|
429
|
-
* `options.checkTakedown` are set to true, respectively).
|
|
430
|
-
*/
|
|
431
|
-
public async findAccount(
|
|
432
|
-
handleOrDid: AtIdentifierString,
|
|
433
|
-
options: VerifiedOptions,
|
|
434
|
-
): Promise<ActorAccount> {
|
|
435
|
-
const account = await this.accountManager.getAccount(handleOrDid, {
|
|
436
|
-
includeDeactivated: true,
|
|
437
|
-
includeTakenDown: true,
|
|
438
|
-
})
|
|
439
|
-
if (!account) {
|
|
440
|
-
// will be turned into ExpiredToken for the client if proxied by entryway
|
|
441
|
-
throw new ForbiddenError('Account not found', 'AccountNotFound')
|
|
442
|
-
}
|
|
443
|
-
if (options.checkTakedown && softDeleted(account)) {
|
|
444
|
-
throw new AuthRequiredError(
|
|
445
|
-
'Account has been taken down',
|
|
446
|
-
'AccountTakedown',
|
|
447
|
-
)
|
|
448
|
-
}
|
|
449
|
-
if (options.checkDeactivated && account.deactivatedAt) {
|
|
450
|
-
throw new AuthRequiredError(
|
|
451
|
-
'Account is deactivated',
|
|
452
|
-
'AccountDeactivated',
|
|
453
|
-
)
|
|
454
|
-
}
|
|
455
|
-
return account
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
/**
|
|
459
|
-
* Wraps {@link jose.jwtVerify} into a function that also validates the token
|
|
460
|
-
* payload's type and wraps errors into {@link InvalidRequestError}.
|
|
461
|
-
*/
|
|
462
|
-
protected async verifyBearerJwt<S extends AuthScope = AuthScope>(
|
|
463
|
-
req: IncomingMessage,
|
|
464
|
-
{ scopes, ...options }: VerifyBearerJwtOptions<S>,
|
|
465
|
-
): Promise<VerifyBearerJwtResult<S>> {
|
|
466
|
-
const token = bearerTokenFromReq(req)
|
|
467
|
-
if (!token) {
|
|
468
|
-
throw new AuthRequiredError(undefined, 'AuthMissing')
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
const { payload } = await jose
|
|
472
|
-
.jwtVerify(token, this._jwtKey, options)
|
|
473
|
-
.catch((cause) => {
|
|
474
|
-
if (cause instanceof jose.errors.JWTExpired) {
|
|
475
|
-
throw new InvalidRequestError('Token has expired', 'ExpiredToken', {
|
|
476
|
-
cause,
|
|
477
|
-
})
|
|
478
|
-
} else {
|
|
479
|
-
throw new InvalidRequestError(
|
|
480
|
-
'Token could not be verified',
|
|
481
|
-
'InvalidToken',
|
|
482
|
-
{ cause },
|
|
483
|
-
)
|
|
484
|
-
}
|
|
485
|
-
})
|
|
486
|
-
|
|
487
|
-
const { sub, aud, scope, lxm, cnf, jti } = payload
|
|
488
|
-
|
|
489
|
-
if (typeof lxm !== 'undefined') {
|
|
490
|
-
// Service auth tokens should never make it to here. But since service
|
|
491
|
-
// auth tokens do not have a "typ" header, the "typ" check above will not
|
|
492
|
-
// catch them. This check here is mainly to protect against the
|
|
493
|
-
// hypothetical case in which a PDS would issue service auth tokens using
|
|
494
|
-
// its private key.
|
|
495
|
-
throw new InvalidRequestError('Malformed token', 'InvalidToken')
|
|
496
|
-
}
|
|
497
|
-
if (typeof cnf !== 'undefined') {
|
|
498
|
-
// Proof-of-Possession (PoP) tokens are not allowed here
|
|
499
|
-
// https://www.rfc-editor.org/rfc/rfc7800.html
|
|
500
|
-
throw new InvalidRequestError('Malformed token', 'InvalidToken')
|
|
501
|
-
}
|
|
502
|
-
if (typeof sub !== 'string' || !isDidString(sub)) {
|
|
503
|
-
throw new InvalidRequestError('Malformed token', 'InvalidToken')
|
|
504
|
-
}
|
|
505
|
-
if (typeof aud !== 'string' || !aud.startsWith('did:')) {
|
|
506
|
-
throw new InvalidRequestError('Malformed token', 'InvalidToken')
|
|
507
|
-
}
|
|
508
|
-
if (typeof jti !== 'string' && typeof jti !== 'undefined') {
|
|
509
|
-
throw new InvalidRequestError('Malformed token', 'InvalidToken')
|
|
510
|
-
}
|
|
511
|
-
if (!isAuthScope(scope) || !scopes.includes(scope as any)) {
|
|
512
|
-
throw new InvalidRequestError('Bad token scope', 'InvalidToken')
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
return { sub, aud, jti, scope: scope as S }
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
protected async verifyServiceJwt(
|
|
519
|
-
req: IncomingMessage,
|
|
520
|
-
opts?: { iss?: string[] },
|
|
521
|
-
) {
|
|
522
|
-
const jwtStr = bearerTokenFromReq(req)
|
|
523
|
-
if (!jwtStr) {
|
|
524
|
-
throw new AuthRequiredError('missing jwt', 'MissingJwt')
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
const nsid = parseReqNsid(req)
|
|
528
|
-
const payload = await verifyServiceJwt(
|
|
529
|
-
jwtStr,
|
|
530
|
-
null,
|
|
531
|
-
nsid,
|
|
532
|
-
async (iss, forceRefresh) => {
|
|
533
|
-
if (opts?.iss && !opts.iss.includes(iss)) {
|
|
534
|
-
throw new AuthRequiredError('Untrusted issuer', 'UntrustedIss')
|
|
535
|
-
}
|
|
536
|
-
const [did, serviceId] = iss.split('#')
|
|
537
|
-
const keyId =
|
|
538
|
-
serviceId === 'atproto_labeler' ? 'atproto_label' : 'atproto'
|
|
539
|
-
const didDoc = await this.idResolver.did.resolve(did, forceRefresh)
|
|
540
|
-
if (!didDoc) {
|
|
541
|
-
throw new AuthRequiredError('could not resolve iss did')
|
|
542
|
-
}
|
|
543
|
-
const parsedKey = getVerificationMaterial(didDoc, keyId)
|
|
544
|
-
if (!parsedKey) {
|
|
545
|
-
throw new AuthRequiredError('missing or bad key in did doc')
|
|
546
|
-
}
|
|
547
|
-
const didKey = getDidKeyFromMultibase(parsedKey)
|
|
548
|
-
if (!didKey) {
|
|
549
|
-
throw new AuthRequiredError('missing or bad key in did doc')
|
|
550
|
-
}
|
|
551
|
-
return didKey
|
|
552
|
-
},
|
|
553
|
-
)
|
|
554
|
-
if (
|
|
555
|
-
payload.aud !== this.dids.pds &&
|
|
556
|
-
(!this.dids.entryway || payload.aud !== this.dids.entryway)
|
|
557
|
-
) {
|
|
558
|
-
throw new AuthRequiredError(
|
|
559
|
-
'jwt audience does not match service did',
|
|
560
|
-
'BadJwtAudience',
|
|
561
|
-
)
|
|
562
|
-
}
|
|
563
|
-
return payload
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
// HELPERS
|
|
568
|
-
// ---------
|
|
569
|
-
|
|
570
|
-
export function isUserOrAdmin(
|
|
571
|
-
auth: AccessOutput | OAuthOutput | AdminTokenOutput | UnauthenticatedOutput,
|
|
572
|
-
did: string,
|
|
573
|
-
): boolean {
|
|
574
|
-
if (!auth.credentials) {
|
|
575
|
-
return false
|
|
576
|
-
} else if (auth.credentials.type === 'admin_token') {
|
|
577
|
-
return true
|
|
578
|
-
} else {
|
|
579
|
-
return auth.credentials.did === did
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
enum AuthType {
|
|
584
|
-
BASIC = 'Basic',
|
|
585
|
-
BEARER = 'Bearer',
|
|
586
|
-
DPOP = 'DPoP',
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
const parseAuthorizationHeader = (
|
|
590
|
-
req: IncomingMessage,
|
|
591
|
-
): [type: null] | [type: AuthType, token: string] => {
|
|
592
|
-
const authorization = req.headers['authorization']
|
|
593
|
-
if (!authorization) return [null]
|
|
594
|
-
|
|
595
|
-
const result = authorization.split(' ')
|
|
596
|
-
if (result.length !== 2) {
|
|
597
|
-
throw new InvalidRequestError(
|
|
598
|
-
'Malformed authorization header',
|
|
599
|
-
'InvalidToken',
|
|
600
|
-
)
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
// authorization type is case-insensitive
|
|
604
|
-
const authType = result[0].toUpperCase()
|
|
605
|
-
|
|
606
|
-
const type = Object.hasOwn(AuthType, authType) ? AuthType[authType] : null
|
|
607
|
-
if (type) return [type, result[1]]
|
|
608
|
-
|
|
609
|
-
throw new InvalidRequestError(
|
|
610
|
-
`Unsupported authorization type: ${result[0]}`,
|
|
611
|
-
'InvalidToken',
|
|
612
|
-
)
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
/**
|
|
616
|
-
* @note Not all service auth tokens are guaranteed to have "lxm" claim, so this
|
|
617
|
-
* function should not be used to verify service auth tokens. It is only used to
|
|
618
|
-
* check if a token is definitely a service auth token.
|
|
619
|
-
*/
|
|
620
|
-
const isDefinitelyServiceAuth = (req: IncomingMessage): boolean => {
|
|
621
|
-
const token = bearerTokenFromReq(req)
|
|
622
|
-
if (!token) return false
|
|
623
|
-
const payload = jose.decodeJwt(token)
|
|
624
|
-
return payload['lxm'] != null
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
const extractAuthType = (req: IncomingMessage): AuthType | null => {
|
|
628
|
-
const [type] = parseAuthorizationHeader(req)
|
|
629
|
-
return type
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
export const bearerTokenFromReq = (req: IncomingMessage) => {
|
|
633
|
-
const [type, token] = parseAuthorizationHeader(req)
|
|
634
|
-
return type === AuthType.BEARER ? token : null
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
const parseBasicAuth = (
|
|
638
|
-
req: IncomingMessage,
|
|
639
|
-
): { username: string; password: string } | null => {
|
|
640
|
-
try {
|
|
641
|
-
const [type, b64] = parseAuthorizationHeader(req)
|
|
642
|
-
if (type !== AuthType.BASIC) return null
|
|
643
|
-
const decoded = Buffer.from(b64, 'base64').toString('utf8')
|
|
644
|
-
// We must not use split(':') because the password can contain colons
|
|
645
|
-
const colon = decoded.indexOf(':')
|
|
646
|
-
if (colon === -1) return null
|
|
647
|
-
const username = decoded.slice(0, colon)
|
|
648
|
-
const password = decoded.slice(colon + 1)
|
|
649
|
-
return { username, password }
|
|
650
|
-
} catch (err) {
|
|
651
|
-
return null
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
export const createSecretKeyObject = (secret: string): KeyObject => {
|
|
656
|
-
return createSecretKey(Buffer.from(secret))
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
const keyEncoder = new KeyEncoder('secp256k1')
|
|
660
|
-
export const createPublicKeyObject = (publicKeyHex: string): KeyObject => {
|
|
661
|
-
const key = keyEncoder.encodePublic(publicKeyHex, 'raw', 'pem')
|
|
662
|
-
return createPublicKey({ format: 'pem', key })
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
function setAuthHeaders(res: ServerResponse) {
|
|
666
|
-
res.setHeader('Cache-Control', 'private')
|
|
667
|
-
appendVary(res, 'Authorization')
|
|
668
|
-
}
|