Project Summary
The RFC proposes a lightweight, industry-standard authentication layer for Forge app developers using Web Triggers. This ensures that functions invoked by publicly accessible Web Trigger URLs are only called by trusted parties.
-
Publish: 6 Aug 2026
-
Discuss:
-
Resolved:
Problem
The Forge Web Trigger capability lets developers create a public URL. When called via HTTP, that URL invokes a backend function in their Forge app. Developers can create these URLs explicitly via the CLI, or programmatically at runtime using the webTrigger module from @forge/api.
Currently, Forge Web Triggers lack native authentication or authorization. Developers must build their own solutions at the application layer, which is error-prone and a common source of security vulnerabilities.
In addition to solving the specific problem of simple, shared-secret authentication, this feature also lays the groundwork to provide a wider variety of more complex built-in authentication mechanisms in the future for developers and admins.
Proposal
We are proposing to support HMAC Authentication for Forge Webtriggers.
This proposal closes 3 key security gaps in the default Web Trigger capability:
-
Authentication: Only clients with a pre-shared secret key can call the URL.
-
Body Tampering: Signatures are computed at the client and re-computed at the server to ensure data integrity.
-
Replay Attacks: Shared secret keys expire and can be rotated. A leaked URL cannot be called indefinitely.
This will be an optional authentication solution, implemented at the platform layer and enabled via modules.webtrigger.request.authentication in your app’s manifest.yml:
# Example manifest.yml
modules:
webtrigger:
- key: hmac-auth-webtrigger
function: auth-webtrigger-fn
urlFormat: v2 # required for this feature
request:
authentication: hmacSharedSecret # valid values include: hmacSharedSecret | none`*
For backwards compatibility, the request field will be optional. Webtriggers that do not have this field defined will continue to work.
| Feature | Details |
|---|---|
| URL format | Authenticated URLs will use a new v2 format with an /auth/ path. |
| Key generation | Secret keys (base64 encoded) are provided via the Forge CLI (piped or interactive) or programmatically via the @forge/api runtime. |
| Authentication header | Requests must include the x-hub-signature header containing an HMAC SHA256 signature of the body, signed with a base64 decoded secret key. |
| Key management | Supports up to 2 keys per installation for rotation. Keys expire after 6 months. Decoded keys must be between 32 and 256 bits. |
# Example Authenticated URL
https://15314aab-9e4a-482e-9db5-fac2eaa17e60.webtrigger.atlassian.app/auth/dUye775SIX5AnlZPckfl2PF5cRs
Note: userPath segments are still supported for authenticated URLs.
URL Generation via CLI
Developers can supply the base64-encoded secret key via an interactive prompt or by piping it to the forge webtrigger create command.
# URL creation via piped input
echo -n "<encoded-shared-secret-key>" | forge webtrigger create --readSecretKey
## OR
# Url creation via interactive input
forge webtrigger create
? Select an installation: my-site.atlassian.net
? Select a web trigger: hmac-auth-webtrigger
? Enter the secret key for this HMAC web trigger: [input is hidden]
<developer types or pastes encoded secret key>
URL Generation via Runtime
The webTrigger module in @forge/api will be updated to accept the encoded shared secret key.
import { webTrigger } from "@forge/api"
async function createHmacAuthWebTrigger() {
const forceCreate = true;
// You can still force create urls with the existing API
// const url = await webTrigger.getUrl('simple-webtrigger', forceCreate);
const url = await webTrigger.getUrl('hmac-auth-webtrigger', {
forceCreate,
secretKey: '<encoded-shared-secret-key>'
});
return url;
}
URL HTTP Authentication
The x-hub-signature will be required for requests to webtriggers that use authentication: hmacSharedSecret.
We chose x-hub-signature because:
-
Using the
Authorizationheader could conflict with existing Web Triggers. -
The
x-hub-signatureheader is the industry standard for this pattern. -
Atlassian already uses it in product webhooks such as Jira.
Clients compute a HMAC SHA256 signature of the HTTP body, using their decoded secret key. This signature is sent in the x-hub-signature header. The server recomputes the signature and compares it to the header value. Requests only succeed when the signatures match.
# Example curl request
signature=$(echo -n <request-body> | openssl dgst -sha256 -hmac <your-decoded-secret-key> | sed -E 's/^SHA2-256\(stdin\)= //g')
# The above 'openssl dgst -sha256 -hmac' command produces a signature of the shape: SHA2-256(stdin)= 8adf40a8b889640b2235c626e988ad3c2c328bd4e6a02860a7418f162e300c92
# The header value provided with the request should omit the prefix 'SHA2-256(stdin)= '
# So, the value provided is just '8adf40a8b889640b2235c626e988ad3c2c328bd4e6a02860a7418f162e300c92'
curl -X POST \
https://15314aab-9e4a-482e-9db5-fac2eaa17e60.webtrigger.atlassian.app/auth/dUye775SIX5AnlZPckfl2PF5cRs \
-H "x-hub-signature: $signature" \
-d <request-body>
Requests without Data
GET requests, or any request with no body, should compute the digest using an empty string. The server uses the same approach when authenticating these requests.
Secret Key Security
A webtrigger module can have at most 2 secret keys registered per product installation. This supports key rotation.
Creating a new URL with the same context (module, installation, app, and environment) and a new secret key will replace the oldest stored key. Both registered keys are tried at invocation time when comparing signatures.
Decoded secret keys must be between 32 and 256 bits long. This range ensures secure signature generation. Key length has no effect on signature generation time, at either the client or server.
Asks
- Like or comment on this RFC if this feature would benefit you or your apps.
- Do you have feedback on the design or the proposed APIs?
- Is there anything we are missing that we could reasonably include?
- Does anyone require a method to determine the age of their registered keys?