Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Bypass GET, as original #1238

Merged
merged 2 commits into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/auth/__tests__/auth.middleware.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ describe('Authorization header: strict', () => {
app.post('/', (req, res) => {
res.json({ hello: 'world' })
})
app.get('/', (req, res) => {
res.json({ hello: 'world' })
})
})

test('allowed DID, valid digest', async () => {
Expand All @@ -74,6 +77,10 @@ describe('Authorization header: strict', () => {
.send(Buffer.from(carFile.bytes)) // Supertest quirk
expect(response.status).toBe(403)
})
test('GET is bypassed', async () => {
const response = await supertest(app).get('/')
expect(response.status).toEqual(200)
})
test('disallowed DID, valid digest', async () => {
const carFile = carFactory.build()
const cid = carFile.put({ hello: 'world' }, { isRoot: true })
Expand Down
104 changes: 53 additions & 51 deletions src/auth/auth.middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,11 @@ CAR_FACTORY.codecs.add(DAG_JOSE)
const VERIFIER = new DID({ resolver: KeyDIDResolver.getResolver() })

enum DISALLOW_REASON {
LAMBDA_INVALID_DIGEST = 'lambda-invalid-digest',
INVALID_DIGEST = 'invalid-digest',
DID_ALLOWLIST_NO_HEADER = 'did-allowlist-no-header',
DID_ALLOWLIST_NO_DID = 'did-allowlist-no-did',
DID_ALLOWLIST_NO_FIELDS = 'did-allowlist-no-fields',
DID_ALLOWLIST_REJECTED = 'did-allowlist-rejected',
DID_ALLOWLIST_INVALID_DIGEST = 'did-allowlist-invalid-digest',
}

export function parseAllowedDIDs(dids: string | undefined): Set<string> {
Expand Down Expand Up @@ -55,59 +54,62 @@ export function auth(opts: AuthOpts): Handler {
* this app will still work if the logice above is not in place.
*/
return async function (req: Request, res: Response, next: NextFunction) {
// const logger = opts.logger

// Use auth lambda
const didFromHeader = req.header('did')
if (didFromHeader && req.body && Object.keys(req.body).length > 0) {
const digest = buildBodyDigest(req.header('Content-Type'), req.body)
if (req.header('digest') === digest) {
ServiceMetrics.count(METRIC_NAMES.AUTH_ALLOWED, 1, { did: didFromHeader })
console.log(`Allowed: Auth lambda: ${didFromHeader}`)
return next()
} else {
console.log(`Disallowed: Auth lambda: Invalid digest`)
return disallow(res, DISALLOW_REASON.LAMBDA_INVALID_DIGEST)
}
}

// Authorization Header
if (hasAllowedDIDsList) {
const authorizationHeader = req.header('Authorization') || ''
const bearerTokenMatch = AUTH_BEARER_REGEXP.exec(authorizationHeader)
const jws = bearerTokenMatch?.[1]
if (!jws) {
console.log(`Disallowed: No authorization header`)
return disallow(res, DISALLOW_REASON.DID_ALLOWLIST_NO_HEADER)
}
const verifyJWSResult = await VERIFIER.verifyJWS(jws)
const did = verifyJWSResult.didResolutionResult.didDocument?.id
if (!did) {
console.log(`Disallowed: No DID`)
return disallow(res, DISALLOW_REASON.DID_ALLOWLIST_NO_DID)
}
const nonce = verifyJWSResult.payload?.['nonce']
const digest = verifyJWSResult.payload?.['digest']
if (!nonce || !digest) {
console.log(`Disallowed: No nonce or No digest`)
return disallow(res, DISALLOW_REASON.DID_ALLOWLIST_NO_FIELDS)
}
if (!isAllowedDID(did, opts)) {
console.log(`Disallowed: ${did}`)
return disallow(res, DISALLOW_REASON.DID_ALLOWLIST_REJECTED)
const logger = opts.logger
const hasBody = req.body && Object.keys(req.body).length > 0

try {
// Use auth lambda
const didFromHeader = req.header('did')
if (didFromHeader && hasBody) {
const digest = buildBodyDigest(req.header('Content-Type'), req.body)
if (req.header('digest') === digest) {
ServiceMetrics.count(METRIC_NAMES.AUTH_ALLOWED, 1, { did: didFromHeader })
logger?.verbose(`Allowed: Auth lambda: ${didFromHeader}`)
return next()
} else {
logger?.verbose(`Disallowed: Auth lambda: Invalid digest`)
return disallow(res, DISALLOW_REASON.INVALID_DIGEST)
}
}

const body = req.body
const contentType = req.header('Content-Type')
const digestCalculated = buildBodyDigest(contentType, body)
if (digestCalculated !== digest) {
console.log(`Disallowed: Incorrect digest for DID ${did}`)
return disallow(res, DISALLOW_REASON.DID_ALLOWLIST_INVALID_DIGEST)
// Authorization Header
if (hasAllowedDIDsList && hasBody) {
const authorizationHeader = req.header('Authorization') || ''
const bearerTokenMatch = AUTH_BEARER_REGEXP.exec(authorizationHeader)
const jws = bearerTokenMatch?.[1]
if (!jws) {
logger?.verbose(`Disallowed: No authorization header`)
return disallow(res, DISALLOW_REASON.DID_ALLOWLIST_NO_HEADER)
}
const verifyJWSResult = await VERIFIER.verifyJWS(jws)
const did = verifyJWSResult.didResolutionResult.didDocument?.id
if (!did) {
logger?.verbose(`Disallowed: No DID`)
return disallow(res, DISALLOW_REASON.DID_ALLOWLIST_NO_DID)
}
const nonce = verifyJWSResult.payload?.['nonce']
const digest = verifyJWSResult.payload?.['digest']
const expectedDigest = buildBodyDigest(req.header('Content-Type'), req.body)
if (!nonce || !digest) {
logger?.verbose(`Disallowed: No nonce or No digest`)
return disallow(res, DISALLOW_REASON.DID_ALLOWLIST_NO_FIELDS)
}
if (digest !== expectedDigest) {
logger?.verbose(`Disallowed: Invalid digest`)
return disallow(res, DISALLOW_REASON.INVALID_DIGEST)
}
if (!isAllowedDID(did, opts)) {
logger?.verbose(`Disallowed: ${did}`)
return disallow(res, DISALLOW_REASON.DID_ALLOWLIST_REJECTED)
}
const relaxedLabel = opts.isRelaxed ? 1 : 0
ServiceMetrics.count(METRIC_NAMES.AUTH_ALLOWED, 1, { did: did, relaxed: relaxedLabel })
}
const relaxedLabel = opts.isRelaxed ? 1 : 0
ServiceMetrics.count(METRIC_NAMES.AUTH_ALLOWED, 1, { did: did, relaxed: relaxedLabel })
return next()
} catch (e: any) {
logger?.err(e.message)
return res.status(500).send('Internal Server Error')
}
return next()
}
}

Expand Down
Loading