Skip to content

Commit

Permalink
fix scopes for account mgiration
Browse files Browse the repository at this point in the history
  • Loading branch information
dholms committed Dec 19, 2024
1 parent 183c057 commit 6b8aa4c
Show file tree
Hide file tree
Showing 14 changed files with 57 additions and 23 deletions.
7 changes: 6 additions & 1 deletion packages/pds/src/account-manager/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -318,20 +318,25 @@ export class AccountManager
if (!user) {
throw new AuthRequiredError('Invalid identifier or password')
}
const isSoftDeleted = softDeleted(user)

let appPassword: password.AppPassDescript | null = null
const validAccountPass = await this.verifyAccountPassword(
user.did,
password,
)
if (!validAccountPass) {
// takendown/suspended accounts cannot login with app password
if (isSoftDeleted) {
throw new AuthRequiredError('Invalid identifier or password')
}
appPassword = await this.verifyAppPassword(user.did, password)
if (appPassword === null) {
throw new AuthRequiredError('Invalid identifier or password')
}
}

return { user, appPassword, isSoftDeleted: softDeleted(user) }
return { user, appPassword, isSoftDeleted }
} finally {
// Mitigate timing attacks
await wait(350 - (Date.now() - start))
Expand Down
5 changes: 4 additions & 1 deletion packages/pds/src/api/app/bsky/actor/getPreferences.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { Server } from '../../../../lexicon'
import AppContext from '../../../../context'
import { AuthScope } from '../../../../auth-verifier'

export default function (server: Server, ctx: AppContext) {
if (!ctx.cfg.bskyAppView) return
server.app.bsky.actor.getPreferences({
auth: ctx.authVerifier.accessStandard(),
auth: ctx.authVerifier.accessStandard({
additional: [AuthScope.Takendown],
}),
handler: async ({ auth }) => {
const requester = auth.credentials.did
const preferences = await ctx.actorStore.read(requester, (store) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import { InvalidRequestError } from '@atproto/xrpc-server'
import AppContext from '../../../../context'
import { Server } from '../../../../lexicon'
import { ids } from '../../../../lexicon/lexicons'
import { AuthScope } from '../../../../auth-verifier'

export default function (server: Server, ctx: AppContext) {
server.com.atproto.identity.requestPlcOperationSignature({
auth: ctx.authVerifier.accessFull(),
auth: ctx.authVerifier.accessFull({ additional: [AuthScope.Takendown] }),
handler: async ({ auth }) => {
if (ctx.entrywayAgent) {
assert(ctx.cfg.entryway)
Expand Down
3 changes: 2 additions & 1 deletion packages/pds/src/api/com/atproto/server/deactivateAccount.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { AuthScope } from '../../../../auth-verifier'
import AppContext from '../../../../context'
import { Server } from '../../../../lexicon'
import { authPassthru } from '../../../proxy'

export default function (server: Server, ctx: AppContext) {
server.com.atproto.server.deactivateAccount({
auth: ctx.authVerifier.accessFull(),
auth: ctx.authVerifier.accessFull({ additional: [AuthScope.Takendown] }),
handler: async ({ req, auth, input }) => {
// in the case of entryway, the full flow is deactivateAccount (PDS) -> deactivateAccount (Entryway) -> updateSubjectStatus(PDS)
if (ctx.entrywayAgent) {
Expand Down
15 changes: 14 additions & 1 deletion packages/pds/src/api/com/atproto/server/getServiceAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,27 @@ import { HOUR, MINUTE } from '@atproto/common'
import AppContext from '../../../../context'
import { Server } from '../../../../lexicon'
import { PRIVILEGED_METHODS, PROTECTED_METHODS } from '../../../../pipethrough'
import { AuthScope } from '../../../../auth-verifier'
import { ids } from '../../../../lexicon/lexicons'

export default function (server: Server, ctx: AppContext) {
server.com.atproto.server.getServiceAuth({
auth: ctx.authVerifier.accessStandard(),
auth: ctx.authVerifier.accessStandard({
additional: [AuthScope.Takendown],
}),
handler: async ({ params, auth }) => {
const did = auth.credentials.did
const { aud, lxm = null } = params
const exp = params.exp ? params.exp * 1000 : undefined

// Takendown accounts should not be able to generate service auth tokens except for methods necessary for account migration
if (
auth.credentials.scope === AuthScope.Takendown &&
lxm !== ids.ComAtprotoServerCreateAccount
) {
throw new InvalidRequestError('Bad token scope', 'InvalidToken')
}

if (exp) {
const diff = exp - Date.now()
if (diff < 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { assertRepoAvailability } from '../util'

export default function (server: Server, ctx: AppContext) {
server.com.atproto.sync.getCheckout({
auth: ctx.authVerifier.optionalAccessOrAdminToken,
auth: ctx.authVerifier.optionalAccessOrAdminToken(),
handler: async ({ params, auth }) => {
const { did } = params
await assertRepoAvailability(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { assertRepoAvailability } from '../util'

export default function (server: Server, ctx: AppContext) {
server.com.atproto.sync.getHead({
auth: ctx.authVerifier.optionalAccessOrAdminToken,
auth: ctx.authVerifier.optionalAccessOrAdminToken(),
handler: async ({ params, auth }) => {
const { did } = params
await assertRepoAvailability(
Expand Down
5 changes: 4 additions & 1 deletion packages/pds/src/api/com/atproto/sync/getBlob.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ import AppContext from '../../../../context'
import { InvalidRequestError } from '@atproto/xrpc-server'
import { BlobNotFoundError } from '@atproto/repo'
import { assertRepoAvailability } from './util'
import { AuthScope } from '../../../../auth-verifier'

export default function (server: Server, ctx: AppContext) {
server.com.atproto.sync.getBlob({
auth: ctx.authVerifier.optionalAccessOrAdminToken,
auth: ctx.authVerifier.optionalAccessOrAdminToken({
additional: [AuthScope.Takendown],
}),
handler: async ({ params, res, auth }) => {
const { did } = params
await assertRepoAvailability(
Expand Down
2 changes: 1 addition & 1 deletion packages/pds/src/api/com/atproto/sync/getBlocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { assertRepoAvailability } from './util'

export default function (server: Server, ctx: AppContext) {
server.com.atproto.sync.getBlocks({
auth: ctx.authVerifier.optionalAccessOrAdminToken,
auth: ctx.authVerifier.optionalAccessOrAdminToken(),
handler: async ({ params, auth }) => {
const { did } = params
await assertRepoAvailability(
Expand Down
2 changes: 1 addition & 1 deletion packages/pds/src/api/com/atproto/sync/getLatestCommit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { assertRepoAvailability } from './util'

export default function (server: Server, ctx: AppContext) {
server.com.atproto.sync.getLatestCommit({
auth: ctx.authVerifier.optionalAccessOrAdminToken,
auth: ctx.authVerifier.optionalAccessOrAdminToken(),
handler: async ({ params, auth }) => {
const { did } = params
await assertRepoAvailability(
Expand Down
2 changes: 1 addition & 1 deletion packages/pds/src/api/com/atproto/sync/getRecord.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { assertRepoAvailability } from './util'

export default function (server: Server, ctx: AppContext) {
server.com.atproto.sync.getRecord({
auth: ctx.authVerifier.optionalAccessOrAdminToken,
auth: ctx.authVerifier.optionalAccessOrAdminToken(),
handler: async ({ params, auth }) => {
const { did, collection, rkey } = params
await assertRepoAvailability(
Expand Down
5 changes: 4 additions & 1 deletion packages/pds/src/api/com/atproto/sync/getRepo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ import {
SqlRepoReader,
} from '../../../../actor-store/repo/sql-repo-reader'
import { assertRepoAvailability } from './util'
import { AuthScope } from '../../../../auth-verifier'

export default function (server: Server, ctx: AppContext) {
server.com.atproto.sync.getRepo({
auth: ctx.authVerifier.optionalAccessOrAdminToken,
auth: ctx.authVerifier.optionalAccessOrAdminToken({
additional: [AuthScope.Takendown],
}),
handler: async ({ params, auth }) => {
const { did, since } = params
await assertRepoAvailability(
Expand Down
5 changes: 4 additions & 1 deletion packages/pds/src/api/com/atproto/sync/listBlobs.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { Server } from '../../../../lexicon'
import AppContext from '../../../../context'
import { assertRepoAvailability } from './util'
import { AuthScope } from '../../../../auth-verifier'

export default function (server: Server, ctx: AppContext) {
server.com.atproto.sync.listBlobs({
auth: ctx.authVerifier.optionalAccessOrAdminToken,
auth: ctx.authVerifier.optionalAccessOrAdminToken({
additional: [AuthScope.Takendown],
}),
handler: async ({ params, auth }) => {
const { did, since, limit, cursor } = params
await assertRepoAvailability(
Expand Down
22 changes: 12 additions & 10 deletions packages/pds/src/auth-verifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,17 +210,19 @@ export class AuthVerifier {
return this.validateAdminToken(ctx)
}

optionalAccessOrAdminToken = async (
ctx: ReqCtx,
): Promise<AccessOutput | AdminTokenOutput | NullOutput> => {
if (isAccessToken(ctx.req)) {
return await this.accessStandard()(ctx)
} else if (isBasicToken(ctx.req)) {
return await this.adminToken(ctx)
} else {
return this.null(ctx)
optionalAccessOrAdminToken =
(opts: Partial<AccessOpts> = {}) =>
async (
ctx: ReqCtx,
): Promise<AccessOutput | AdminTokenOutput | NullOutput> => {
if (isAccessToken(ctx.req)) {
return await this.accessStandard(opts)(ctx)
} else if (isBasicToken(ctx.req)) {
return await this.adminToken(ctx)
} else {
return this.null(ctx)
}
}
}

userServiceAuth = async (ctx: ReqCtx): Promise<UserServiceAuthOutput> => {
const payload = await this.verifyServiceJwt(ctx, {
Expand Down

0 comments on commit 6b8aa4c

Please sign in to comment.