Skip Navigation
Show nav
Dev Center
  • Get Started
  • Documentation
  • Changelog
  • Search
  • Get Started
    • Node.js
    • Ruby on Rails
    • Ruby
    • Python
    • Java
    • PHP
    • Go
    • Scala
    • Clojure
    • .NET
  • Documentation
  • Changelog
  • More
    Additional Resources
    • Home
    • Elements
    • Products
    • Pricing
    • Careers
    • Help
    • Status
    • Events
    • Podcasts
    • Compliance Center
    Heroku Blog

    Heroku Blog

    Find out what's new with Heroku on our blog.

    Visit Blog
  • Log inorSign up
Hide categories

Categories

  • Heroku Architecture
    • Compute (Dynos)
      • Dyno Management
      • Dyno Concepts
      • Dyno Behavior
      • Dyno Reference
      • Dyno Troubleshooting
    • Stacks (operating system images)
    • Networking & DNS
    • Platform Policies
    • Platform Principles
  • Developer Tools
    • Command Line
    • Heroku VS Code Extension
  • Deployment
    • Deploying with Git
    • Deploying with Docker
    • Deployment Integrations
  • Continuous Delivery & Integration (Heroku Flow)
    • Continuous Integration
  • Language Support
    • Node.js
      • Troubleshooting Node.js Apps
      • Working with Node.js
      • Node.js Behavior in Heroku
    • Ruby
      • Rails Support
      • Working with Bundler
      • Working with Ruby
      • Ruby Behavior in Heroku
      • Troubleshooting Ruby Apps
    • Python
      • Working with Python
      • Background Jobs in Python
      • Python Behavior in Heroku
      • Working with Django
    • Java
      • Java Behavior in Heroku
      • Working with Java
      • Working with Maven
      • Working with Spring Boot
      • Troubleshooting Java Apps
    • PHP
      • PHP Behavior in Heroku
      • Working with PHP
    • Go
      • Go Dependency Management
    • Scala
    • Clojure
    • .NET
      • Working with .NET
  • Databases & Data Management
    • Heroku Postgres
      • Postgres Basics
      • Postgres Getting Started
      • Postgres Performance
      • Postgres Data Transfer & Preservation
      • Postgres Availability
      • Postgres Special Topics
      • Migrating to Heroku Postgres
    • Heroku Key-Value Store
    • Apache Kafka on Heroku
    • Other Data Stores
  • AI
    • Vector Database
    • Working with AI
    • Heroku Inference
      • AI Models
      • Inference Essentials
      • Inference API
      • Quick Start Guides
    • Model Context Protocol
  • Monitoring & Metrics
    • Logging
  • App Performance
  • Add-ons
    • All Add-ons
  • Collaboration
  • Security
    • App Security
    • Identities & Authentication
      • Single Sign-on (SSO)
    • Private Spaces
      • Infrastructure Networking
    • Compliance
  • Heroku Enterprise
    • Enterprise Accounts
    • Enterprise Teams
    • Heroku Connect (Salesforce sync)
      • Heroku Connect Administration
      • Heroku Connect Reference
      • Heroku Connect Troubleshooting
  • Patterns & Best Practices
  • Extending Heroku
    • Platform API
    • App Webhooks
    • Heroku Labs
    • Building Add-ons
      • Add-on Development Tasks
      • Add-on APIs
      • Add-on Guidelines & Requirements
    • Building CLI Plugins
    • Developing Buildpacks
    • Dev Center
  • Accounts & Billing
  • Troubleshooting & Support
  • Integrating with Salesforce
  • Language Support
  • Go
  • OAuth2 with Heroku & Go

OAuth2 with Heroku & Go

English — 日本語に切り替える

Last updated January 26, 2022

Table of Contents

  • Prerequisites
  • The sample application
  • OAuth Client Setup
  • App Setup
  • Global Variables
  • init function
  • Handlers
  • main function
  • Visiting the App
  • Summary

This tutorial demonstrates how to create a web application on Heroku that lets users authorize using the Heroku platform’s OAuth API, and then perform API calls to api.heroku.com. It can serve as the basis of a more complex integration scenario.

The tutorial uses the Go language, but the main concepts, configuration and architecture can be just as easily applied to any other language supported by Heroku.

Sample code for the demo application is available on GitHub. Edits and enhancements are welcome. Just fork the repository, make your changes and send us a pull request.

Prerequisites

  • A Heroku user account. Signup is free and instant.
  • If you haven’t already, follow Getting Started with Go on Heroku to familiarize yourself with Heroku.

The sample application

The sample app demonstrates the main components of authorization and making API calls. The rest of this tutorial will highlight various parts of the app.

To get started, clone the application:

$ go get -u github.com/heroku-examples/heroku-oauth-example-go
$ cd $GOPATH/src/github.com/heroku-examples/heroku-oauth-example-go

OAuth Client Setup

$ heroku clients:create  "Go OAuth Example ($USER)" https://go-heroku-oauth-example-$USER.herokuapp.com/auth/heroku/callback

Note the provided HEROKU_OAUTH_ID and HEROKU_OAUTH_SECRET output, this is used below. These are used to identify and authenticate the sample application to heroku.

App Setup

$ heroku create go-heroku-oauth-example-$USER
$ heroku labs:enable runtime-dyno-metadata
$ heroku config:add HEROKU_OAUTH_ID=<value from above>
$ heroku config:add HEROKU_OAUTH_SECRET=<value from above>
$ heroku config:add COOKIE_SECRET=`openssl rand -hex 32`
$ heroku config:add COOKIE_ENCRYPT=`openssl rand -hex 16`
$ git push heroku master
$ heroku open

The COOKIE_* config vars are used to secure the cookies created by the sample app.

At this point you should have a working application and your web browser open to a page that says “Sign in with Heroku”.

Before continuing let’s look at the code behind the sample application.

Global Variables

Since this is a simple example application it uses a few global variables. In a normal application these would be defined on a struct implementing http.Handler or a similar, framework specific struct.

var (
    store = sessions.NewCookieStore([]byte(os.Getenv("COOKIE_SECRET")), []byte(os.Getenv("COOKIE_ENCRYPT")))

    oauthConfig = &oauth2.Config{
        ClientID:     os.Getenv("HEROKU_OAUTH_ID"),
        ClientSecret: os.Getenv("HEROKU_OAUTH_SECRET"),
        Endpoint:     heroku.Endpoint,
        Scopes:       []string{"identity"},
        RedirectURL:  "http://" + os.Getenv("HEROKU_APP_NAME") + "herokuapp.com/auth/heroku/callback",
    }

    stateToken = os.Getenv("HEROKU_APP_NAME")
)

func init() {
    gob.Register(&oauth2.Token{})

    store.MaxAge(60 * 60 * 8)
    store.Options.Secure = true
}

store is a cookie based session store. The sample app saves it’s intermediate data in a secure cookie.

oauthConfig is an oauth2.Config setup for your instance of the sample application. You can find more information about supported scopes here.

stateToken is used during the ouath2 handshake.This would normally be set to an opaque value that can be checked by the applications during the oauth2 handshake.

init function

The init function:

  1. Registers the oauth2.Token type with gob so the token we receive can be serialized into a cookie later.
  2. Sets up the store to expire in 8 hours and ensures that cookies are only sent via https.

Handlers

func handleRoot(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, `<html><body><a href="/auth/heroku">Sign in with Heroku</a></body></html>`)
}

The handleRoot function displays our simple index page.

func handleAuth(w http.ResponseWriter, r *http.Request) {
    url := oauthConfig.AuthCodeURL(stateToken)
    http.Redirect(w, r, url, http.StatusFound)
}

The handleAuth function uses oauthConfig to create an url pointing to Heroku’s consent page and then redirects the user to this page. This is the first part of the OAuth interaction.

func handleAuthCallback(w http.ResponseWriter, r *http.Request) {
    if v := r.FormValue("state"); v != stateToken {
        http.Error(w, "Invalid State token", http.StatusBadRequest)
        return
    }
    ctx := context.Background()
    token, err := oauthConfig.Exchange(ctx, r.FormValue("code"))
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    session, err := store.Get(r, "heroku-oauth-example-go")
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    session.Values["heroku-oauth-token"] = token
    if err := session.Save(r, w); err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    http.Redirect(w, r, "/user", http.StatusFound)
}

The handleAuthCallback is the second and core part of the OAuth interaction. First the state form value is retrieved and checked against the set stateToken. If it’s not the same, the request is bad. Next the app uses the background context (In a real application an application specific context would be used.) to perform the OAuth2 exchange, converting the provided code to a token. Next the app stores the token in session and save the session into the cookie. Last we’re redirected to the /user route.

func handleUser(w http.ResponseWriter, r *http.Request) {
    session, err := store.Get(r, "heroku-oauth-example-go")
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    token, ok := session.Values["heroku-oauth-token"].(*oauth2.Token)
    if !ok {
        http.Error(w, "Unable to assert token", http.StatusInternalServerError)
        return
    }
    client := oauthConfig.Client(context.Background(), token)
    resp, err := client.Get("https://api.heroku.com/account")
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    defer resp.Body.Close()
    d := json.NewDecoder(resp.Body)
    var account struct { // See https://devcenter.heroku.com/articles/platform-api-reference#account
        Email string `json:"email"`
    }
    if err := d.Decode(&account); err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    fmt.Fprintf(w, `<html><body><h1>Hello %s</h1></body></html>`, account.Email)
}

The handleUser function shows how to use the oauth2.Client method paired with a token to call the Heroku API. The function starts out by retrieving the token from the session. Next it creates a http.Client using the [oauth2.Client](https://godoc.org/golang.org/x/oauth2#Config.Client) method to fetch the user’s account information from the Heroku API. Last it says hello to the user.

func main() {
    http.HandleFunc("/", handleRoot)
    http.HandleFunc("/auth/heroku", handleAuth)
    http.HandleFunc("/auth/heroku/callback", handleAuthCallback)
    http.HandleFunc("/user", handleUser)
    http.ListenAndServe(":"+os.Getenv("PORT"), nil)

main function

The main function is your entry point into the program and wires up the above handlers into the default http Mux and starts a http server on $PORT.

Visiting the App

When you click on Sign in with Heroku, your browser requests /auth/heroku, which generates the oauth2 provider URL and redirects you to https://id.heroku.com for login. Once you have authorized your instance of the sample app, id.heroku.com redirects you back to your sample app’s /auth/heroku/callback with the appropriate oauth2 state and code parameters. And finally this redirects you to a page that says Hello <your email address>.

Summary

This tutorial demonstrates how to create a Heroku web application that uses Heroku’s OAuth2 API for authentication and how to then use the provided OAuth2 token to authenticate to Heroku’s API.

Keep reading

  • Go

Feedback

Log in to submit feedback.

Using WebSockets on Heroku with Go Scaling a Gin Application with Memcache

Information & Support

  • Getting Started
  • Documentation
  • Changelog
  • Compliance Center
  • Training & Education
  • Blog
  • Support Channels
  • Status

Language Reference

  • Node.js
  • Ruby
  • Java
  • PHP
  • Python
  • Go
  • Scala
  • Clojure
  • .NET

Other Resources

  • Careers
  • Elements
  • Products
  • Pricing
  • RSS
    • Dev Center Articles
    • Dev Center Changelog
    • Heroku Blog
    • Heroku News Blog
    • Heroku Engineering Blog
  • Twitter
    • Dev Center Articles
    • Dev Center Changelog
    • Heroku
    • Heroku Status
  • Github
  • LinkedIn
  • © 2025 Salesforce, Inc. All rights reserved. Various trademarks held by their respective owners. Salesforce Tower, 415 Mission Street, 3rd Floor, San Francisco, CA 94105, United States
  • heroku.com
  • Legal
  • Terms of Service
  • Privacy Information
  • Responsible Disclosure
  • Trust
  • Contact
  • Cookie Preferences
  • Your Privacy Choices