Build a simple Golang OAuth client to access Qlik Sense SaaS

1 Introduction

This tutorial shows you how to use the Golang OAuth client with Qlik Sense SaaS.

2 Prerequisites

  1. Using the Management Console, create an OAuth client with your callback URL as the redirect URL.
  2. Save client ID and client secret. This is needed for configuration below.

More details are available at

Create an OAuth Client

3 Setup

3.1 Configure Golang client

Create a Golang OAuth client with both the client application information and the server's endpoint URLs, for example:

func newOAuth2Client() *oauth.Config {
  return &oauth.Config{ // https://pkg.go.dev/golang.org/x/oauth2#Config
    ClientID:     clientId, // read from env var CLIENT_ID
    ClientSecret: clientSecret, // read from env var CLIENT_SECRET
    RedirectURL:  redirectUrl,    // your callback URL to get the authorization code, e.g. http://oauthclient.test:1556/callback
    Scopes:       []string{"offline_access", "user_default"}, // default scopes of Qlik Sense SaaS
    // qcsTenant read from env var QCS_TENANT
    Endpoint: oauth.Endpoint{
      TokenURL:  qcsTenant + "/oauth/token",     // token endpoint of Qlik Sense SaaS
      AuthURL:   qcsTenant + "/oauth/authorize", // authorization endpoint of Qlik Sense SaaS
      AuthStyle: oauth.AuthStyleInHeader,        // use client_id and client_secret as basic authentication header https://pkg.go.dev/golang.org/x/oauth2#AuthStyle
    },
  }
}

3.2 Request an authorization code

Handle the sign-in request by redirecting to the authorize request URL of Qlik Sense SaaS, for example:

func handleAuthorize(w http.ResponseWriter, r *http.Request) {
  codeVerifier := GenerateCodeVerifier(43) // minimum length 43, save it to complete pkce
  codeChallenge := PkceChallenge(codeVerifier)
  url := oauthClient.AuthCodeURL(oauthState,                                                // generate authorization URL https://pkg.go.dev/golang.org/x/oauth2#Config.AuthCodeURL
    oauth.SetAuthURLParam("code_challenge_method", "S256"),                                 // make sure pass in PKCE params
    oauth.SetAuthURLParam("code_challenge", codeChallenge))
  http.Redirect(w, r, url, http.StatusFound) // redirect to authorize request URL
}

var whitelistChars = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~")

func randString(n int) string {
  b := make([]rune, n)
  for i := range b {
    b[i] = whitelistChars[rand.Intn(len(whitelistChars))]
  }
  return string(b)
}

func GenerateCodeVerifier(length int) string {
  if length > 128 {
    length = 128
  }
  if length < 43 {
    length = 43
  }
  return randString(length)
}

func PkceChallenge(verifier string) string {
  sum := sha256.Sum256([]byte(verifier))
  challenge := base64.URLEncoding.WithPadding(base64.NoPadding).EncodeToString(sum[:])
  return (challenge)
}

3.3 Exchange the code for tokens

Handle the callback from Qlik Sense SaaS by using the returned authorization code to exchange tokens, for example:

func handleCallback(w http.ResponseWriter, r *http.Request) {
  tokens, err := exchangeTokens(r.FormValue("state"), r.FormValue("code")) // pick up state and code from callback of Qlik Sense SaaS
  if err != nil {
    fmt.Println(err.Error())
    http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
    return
  }

  // save the tokens in cache or db
}

func exchangeTokens(state string, code string) (*oauth.Token, error) {
  if state != oauthState { // check original state matching
    return nil, fmt.Errorf("invalid oauth state")
  }

  tokens, err := oauthClient.Exchange(context.Background(), code, // https://pkg.go.dev/golang.org/x/oauth2#Config.Exchange
    oauth.SetAuthURLParam("code_verifier", codeVerifier), // complete PKCE by passing saved code_verifier
    oauth.SetAuthURLParam("client_id", clientId))       // make sure pass in client_id

  if err != nil {
    return nil, fmt.Errorf("code exchange failed: %s", err.Error())
  }
  return tokens, nil
}

3.4 Calling APIs of Qlik Sense SaaS with token

Use the saved access token to make an API request to Qlik Sense SaaS, for example:

func accessQCS(accessToken string) {
  bearer := "Bearer " + accessToken
  client := http.Client{Timeout: 5 * time.Second}
  req, err := http.NewRequest(http.MethodGet, qcsTenant+"/api/v1/users/me", http.NoBody)
  if err != nil {
    fmt.Println(err.Error())
    return
  }

  req.Header.Add("Authorization", bearer) // use access token as bearer token
  response, err := client.Do(req)
  if err != nil {
    fmt.Println(err.Error())
    return
  }

  defer response.Body.Close()
  userInfo, err := ioutil.ReadAll(response.Body)
  if err != nil {
    fmt.Println(err.Error())
    return
  }

  dst := &bytes.Buffer{}
  if err := json.Indent(dst, userInfo, "", "  "); err != nil {
    fmt.Println(err.Error())
    return
  }

  fmt.Println(dst.String())
}
Was this page helpful?