Add JWT callback verification to SdrsAuthenticationService
Add SDRS callback JWT verification support
What does this MR do and why?
Followed threat model https://gitlab.com/gitlab-com/gl-security/product-security/appsec/appsec-reviews/-/issues/249
This MR adds JWT callback token verification to the SdrsAuthenticationService to enable secure asynchronous communication from the Secret Detection Response Service (SDRS) back to GitLab.
Background
The Secret Detection feature needs to verify partner tokens through an external service (SDRS). The flow is:
- GitLab sends a verification request to SDRS with a JWT token
- SDRS processes the request asynchronously
- SDRS callbacks to GitLab with the verification result (active/revoked)
This MR implements step 3 - secure callback verification.
Changes
-
Add
verify_callback_tokenmethod: Validates JWT tokens from SDRS with proper security checks - Implement replay attack prevention: Uses Redis to track JWT IDs (JTI) and prevent token reuse
- Add clock skew tolerance: 30-second leeway for distributed system time differences
-
Validate GitLab claims: Ensures
finding_idis present and valid - Add comprehensive error handling: Logs JWT validation failures without exposing sensitive data
Screenshots or screen recordings
N/A - Backend service changes only
How to set up and validate locally
Prerequisites
-
Enable SDRS feature flag (if applicable):
Feature.enable(:sdrs_partner_token_verification) -
Configure SDRS keys in Rails console:
# Generate keys for testing gitlab_key = OpenSSL::PKey::RSA.generate(2048) sdrs_key = OpenSSL::PKey::RSA.generate(2048) # Set GitLab's signing key (for outgoing requests) ApplicationSetting.current.update!(sdrs_jwt_signing_key: gitlab_key.to_pem) # Set SDRS's public key (for verifying callbacks) ApplicationSetting.current.update!(sdrs_jwt_public_key: sdrs_key.public_key.to_pem)
Test Flows
1. Test Token Generation (GitLab → SDRS)
# In Rails console
user = User.first
project = Project.first
finding = Vulnerabilities::Finding.first
# Generate token for SDRS
token = Authz::SdrsAuthenticationService.generate_token(
user: user,
project: project,
finding_id: finding.id
)
puts "Token for SDRS: #{token}"
# Decode to verify contents
public_key = OpenSSL::PKey::RSA.new(ApplicationSetting.current.sdrs_jwt_signing_key).public_key
decoded = JWT.decode(token, public_key, true, algorithm: 'RS256')
pp decoded
2. Test Valid Callback Verification (SDRS → GitLab)
# Simulate SDRS callback
sdrs_private_key = OpenSSL::PKey::RSA.new(ApplicationSetting.current.sdrs_jwt_public_key)
# Create callback JWT
callback_payload = {
iss: 'sdrs',
aud: 'gitlab-secret-detection',
exp: 1.hour.from_now.to_i,
iat: Time.current.to_i,
jti: SecureRandom.uuid,
gitlab: {
finding_id: finding.id
}
}
callback_token = JWT.encode(callback_payload, sdrs_private_key, 'RS256')
# Verify the callback
result = Authz::SdrsAuthenticationService.verify_callback_token(callback_token)
puts "Verification result: #{result.inspect}"
# Should return the payload
MR acceptance checklist
This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.
-
I have evaluated the MR acceptance checklist for this MR.
Related Issues
Edited by Aditya Tiwari