Last updated February 19, 2026
Your cloud service likely has a web UI admin panel that your users log into to manage their resources. For example, a Memcache cloud service can offer a dashboard for web-based usage analytics and the ability to flush a cache.
Heroku customers can access your dashboard as well if you implement single sign-on as described in this document.
Summary
Heroku generates single sign-on tokens by combining the salt (a shared secret), timestamp, resource ID, user ID, and user email. The user’s browser redirects to your site with these tokens. Your site can confirm the authenticity of the tokens, then create a user session and route them to the admin panel for their add-on resource.
Testing SSO
We suggest creating a <your slug>-staging version of your add-on to test SSO and other add-on integration features. Point this <your slug>-staging add-on
to staging versions of your integration infrastructure.
Salt, Timestamp, and Tokens
The add-on manifest is a JSON document that describes your add-on.
When you create an add-on (and consequently its manifest), the manifest fields include a randomly-generated sso_salt.
There are three tokens in an SSO request, and any may be used to log in the user, depending on what version of the Add-on Partner API you’re using. The recommended v3 token is user_scoped_resource_token. There is also resource_token available on v3, though user_scoped_resource_token is the preferred token because it includes details on the requestor.
Creating the Resource Token
Under v3 of the Add-on Partner API, we create the user_scoped_resource_token using the following formula:
user_scoped_resource_token = sha256(resource_uuid + ':' + salt + ':' + timestamp + ':' + user_uuid + ':' + user_email)
resource_uuidis the UUID that we provide for the resource in a provisioning request.saltcomes from your add-on manifest.timestampis included in the parameters for thePOSTrequest.user_uuidanduser_emailare the UUID and email, respectively, of the user initiating the specified add-on’s SSO request.
We create the resource_token with this formula:
resource_token = sha1(resource_uuid + ':' + salt + ':' + timestamp)
For example, given these inputs:
resource_uuid = 11111111-1111-1111-1111-111111111111
salt = 2f97bfa52ca102f8874716e2eb1d3b4920ad0be4
timestamp = 1267597772
user_uuid = 22222222-2222-2222-2222-222222222222
user_email = user_sso@heroku.com
The SSO user_scoped_resource_token is:
SHA256("11111111-1111-1111-1111-111111111111:2f97bfa52ca102f8874716e2eb1d3b4920ad0be4:1267597772:22222222-2222-2222-2222-222222222222:user_sso@heroku.com") =
3f0df24db439d7g4f3968c8ef2835ge7d8d2d534
And the SSO resource_token is:
SHA1("11111111-1111-1111-1111-111111111111:2f97bfa52ca102f8874716e2eb1d3b4920ad0be4:1267597772") =
4e9ce13ca328c6f3e2857b7de1724fd6c7c1c423
Signing in the User on Redirect
When the user clicks your add-on in their add-on menu, they get directed via HTTP POST to a URL defined in your manifest.
Requests look like:
POST <production/sso_url>
resource_id=<resource_id>&resource_token=<resource_token>&id=<id>&token=<token>×tamp=<timestamp>&nav-data=<nav data>&user_id=<user_id>&email=<user's email address>&user_scoped_resource_token=<user_scoped_resource_token>nav-data=<nav data>&email=<user's email address>
As shown, the data is form-encoded in the POST body.
sso_urlcomes from your add-on manifest.resource_idis the UUID that we provide for the resource in the provisioning call.timestampis a unix epoch timestamp.resource_tokenis computed using the formula above. When using theresource_idandresource_token, theidandtokenvalues get ignored.user_idis the UUID that we provide for the user initiating the SSO request.emailis the email address of the user initiating the SSO request. Use this email address over the one provided in the navigation data to prevent email spoofing.user_scoped_resource_tokenis computed per the formula above. When using theresource_idanduser_scoped_resource_token, theidandtokenvalues get ignored.
Legacy fields that you can safely ignore are:
idandtokenare used only for the v1 salted token. Ignore if you’re on v3 of the Add-on Partner API.nav-datacontains information like the current app name and installed add-ons so the Heroku layout can build the appropriate view for the current app.
If the SHA256 hash you compute doesn’t match the one passed in user_scoped_resource_token, or the SHA1 hash you compute doesn’t match the one passed in resource_token, show the user a page with an HTTP status code of 403. If the timestamp is older than five minutes, they also see a 403.
HTTP status code 403 indicates that the user isn’t allowed access to this page. You can return this code and still render a normal, human-readable page for them, perhaps suggesting that they contact support if they believe their request is legitimate.
If the timestamp is current and the SHA256 or SHA1 hashes match, the user is authorized to access the resource in question.
Create a user session through whatever method you normally use, possibly setting a cookie. The session must store that it’s a Heroku single sign-on, because what’s displayed is slightly different for Heroku customers than users logging in through your regular standalone service. Because user access can change at any time, only trust this SSO data for the duration of a session. We also suggest limiting session lifespan to 90 minutes.
Display User Information in Your site’s Layout
There are additional form-encoded parameters sent in the SSO request to help your site display context for the user. These parameters include:
user: the email address of the current user.app: the Heroku app name the user came from.
Use these parameters to populate a template for the user info (and potentially fetch their Gravatar), as well as link back to the dashboard for the app in context. For that, assuming the param came in as &app=your-app-name, link to https://dashboard.heroku.com/apps/your-app-name.
Sample Code
Here’s a sample implementation of a v3 single sign-on endpoint written in Ruby/Sinatra using the recommended user_scoped_resource_token:
post "/heroku/sso" do
pre_user_token = params[:resource_id] + ':' + HEROKU_SSO_SALT + ':' + params[:timestamp] + ':' + params[:user_uuid] + ':' + params[:email]
user_token = OpenSSL::HMAC.hexdigest("SHA256", HEROKU_SSO_SALT, (pre_user_token).to_s)
halt 403 if user_token != params[:user_scoped_resource_token]
halt 403 if params[:timestamp].to_i < (Time.now - 2*60).to_i
account = Account.find(params[:user_id])
halt 404 unless account
session[:user] = account.id
session[:heroku_sso] = true
redirect "/dashboard"
end
Here’s a sample implementation using the older resource_token:
post "/heroku/sso" do
pre_token = params[:resource_id] + ':' + HEROKU_SSO_SALT + ':' + params[:timestamp]
token = Digest::SHA1.hexdigest(pre_token).to_s
halt 403 if token != params[:resource_token]
halt 403 if params[:timestamp].to_i < (Time.now - 2*60).to_i
account = Account.find(params[:resource_id])
halt 404 unless account
session[:user] = account.id
session[:heroku_sso] = true
redirect "/dashboard"
end
Removing Non-Relevant Page Elements
After you have your site accepting single sign-on requests, the final step is to hide or disable page elements not relevant for Heroku customer SSO sessions. Some examples include:
- Change password
- Change account name
- Update billing information
- Log out
Heroku customers manage all of these items through the Heroku Dashboard and CLI, and your add-on can’t update them in Heroku’s infrastructure.