Skip to content

Commit

Permalink
Add hmac authentication to beckn webhook (pupilfirst#1656)
Browse files Browse the repository at this point in the history
  • Loading branch information
yash-learner authored Jul 9, 2024
1 parent 13ce2e2 commit b33e5f3
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 3 deletions.
6 changes: 5 additions & 1 deletion app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,11 @@ def api_token

# Authorization headers are of format "Authorization: <type> <credentials>".
# We only care about the supplied credentials.
header.split(" ")[-1] if header.present?
if header&.starts_with?("HMAC")
# skip: do nothing this is a webhook request
elsif header.present?
header.split(" ")[-1]
end
end
end

Expand Down
44 changes: 42 additions & 2 deletions app/controllers/inbound_webhooks/beckn_controller.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
class InboundWebhooks::BecknController < ApplicationController
skip_before_action :verify_authenticity_token
# before_action :verify_signature
before_action :verify_signature

# POST /inbound_webhook/beckn
def create
Expand All @@ -21,10 +21,50 @@ def create
private

def verify_signature
# TODO: Add authentication for the Beckn webhook; Waiting on https://github.com/beckn/protocol-server/issues/152
return if secret.blank?

auth_header = request.headers["authorization"]&.strip

if auth_header.blank?
return(
render json: {
message: "Missing authorization header"
},
status: :unauthorized
)
end

unless auth_header.starts_with?("HMAC-SHA-256")
return(
render json: {
message: "Invalid signature format"
},
status: :unauthorized
)
end

received_hmac = auth_header.split.last
expected_hmac = hmac(secret, payload)

unless ActiveSupport::SecurityUtils.secure_compare(
received_hmac,
expected_hmac
)
return(
render json: { message: "Invalid signature" }, status: :unauthorized
)
end
end

def payload
@payload ||= request.body.read
end

def secret
@secret ||= Rails.application.secrets.beckn[:webhook_hmac_key]
end

def hmac(secret, payload)
OpenSSL::HMAC.hexdigest("SHA256", secret, payload)
end
end
1 change: 1 addition & 0 deletions config/secrets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ common: &common
bpp_id: <%= ENV['BECKN_BPP_ID'] %>
bpp_uri: <%= ENV['BECKN_BPP_URI'] %>
bpp_client_uri: <%= ENV['BECKN_BPP_CLIENT_URI'] %>
webhook_hmac_key: <%= ENV['BECKN_WEBHOOK_HMAC_KEY'] %>

development:
secret_key_base: "2ab04e6d7919f4f9fd1e25d41455aa26ad21c2a8d053bc00ac02db4d424d97e0716105c620907e6d829329fe275d52673117d432d6d00c9052bec26a82b2de3f"
Expand Down
20 changes: 20 additions & 0 deletions docs/developers/beckn.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,22 @@ Set the following environment variables:
# Domain configured for Beckn API.
BECKN_DOMAIN=your_beckn_webhook_domain

# Optional: webhook authentication, 256-bit key.
BECKN_WEBHOOK_HMAC_KEY=your_beckn_webhook_hmac_key

# Beckn Credentials
BECKN_BPP_ID=beckn_bpp_id
BECKN_BPP_URI=beckn_bpp_uri
BECKN_BPP_CLIENT_URI=beckn_bpp_client_uri
```

> **Note:** If you are using authentication for webhook, update the `default.yml` file of BPP client:
```yml
useHMACForWebhook: true
sharedKeyForWebhookHMAC: your_beckn_webhook_hmac_key
```
## Configuration
Use the following steps to enable Beckn in your school and courses. These commands are to be run in the Rails console.
Expand Down Expand Up @@ -231,12 +241,22 @@ We should create a new file for each domain we are using in the Beckn protocol.
# Ignore this variable if multi-tenancy is disabled in development.
BECKN_DOMAIN=beckn.localhost

# Optional: webhook authentication, 256-bit key.
BECKN_WEBHOOK_HMAC_KEY=your_beckn_webhook_hmac_key

# Beckn Credentials
BECKN_BPP_ID=bpp-bodhi-pf
BECKN_BPP_URI=https://bpp-bodhi.loca.lt
BECKN_BPP_CLIENT_URI=http://localhost:6001
```

> **Note:** If you are using authentication for webhook, update the `default.yml` file of BPP client:
```yml
useHMACForWebhook: true
sharedKeyForWebhookHMAC: your_beckn_webhook_hmac_key
```
## Server Start
Ensure the server is running on the same port as the one set in the webhook URL.
Expand Down
11 changes: 11 additions & 0 deletions example.env
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,14 @@ SCHOOL_ID_FOR_DISCORD_BOT=1

# A flag to enable/disable maintenance mode
ENABLE_MAINTENANCE_MODE=false

# Domain configured for Beckn API.
BECKN_DOMAIN=your_beckn_webhook_domain

# Optional: webhook authentication, 256-bit key.
BECKN_WEBHOOK_HMAC_KEY=your_beckn_webhook_hmac_key

# Beckn Credentials
BECKN_BPP_ID=beckn_bpp_id
BECKN_BPP_URI=beckn_bpp_uri
BECKN_BPP_CLIENT_URI=beckn_bpp_client_uri

0 comments on commit b33e5f3

Please sign in to comment.