Authenticating with Salsify via OAuth 2.0 

Overview

There are two ways to authenticate with the Salsify API: authentication via OAuth 2.0 and authentication by passing HTTP Authorization  headers.  

We also support passing the authentication token via an access_token query parameter: 

http://app.salsify.com/api/v1/products/1234?access_token=<your auth token>

A code example for the OAuth method is below.

For more details on integrating with Salsify, click here.

OAuth Setup

  1. Provide Salsify with your prefered URI to receive your client_id and client_secret.  Contact your Customer Champion or email success@salsify.com.
  2. We will provide you with a client_id and client_secret, which you can use to kick off the flow.
  3. Make the authorization request to get the authorization code as a response.

    Send a GET request to:
    https://app.salsify.com/oauth/authorize?redirect_uri=<your_preferred_redirect_uri>&response_type=code&client_id=<we_provide_you_with_this>

    You should get an authorization code in the response.

  4. Exchange the authorization code for an access token.

    Send a POST request to: https://app.salsify.com/oauth/token

    Include in the BODY:
    code=(the authorization code you get sent back from Step #1)
    grant_type=authorization_code
    redirect_uri=<your_preferred_redirect_uri>

    Include in the HEADER:
    client_id=<we_provide_you_with_this>
    client_secret=<we_provide_you_with_this>

    You should get an access token in the response, which is what you’ll include in the header of your requests to our API.

We imagine there will be some back and forth between our teams to confirm that OAuth is working properly, but this is something we’re happy to assist with.

Example Code


#!/usr/bin/env ruby
require 'sinatra'
require 'sinatra/reloader'
 
# Access tokens are stored in a session variable, encrypted in a cookie.
# This removes the burden of maintaining sessions across multiple servers.
enable :sessions
 
# This is the callback configured for the "Local Sinatra Application" registered on production.
# Use this route and the given UID/secret values below instead of registering a new application
# for local use.
CALLBACK = "http://localhost:4567/auth/callback".freeze
 
# OAuth application UID. Can be obtained from production Rails console via:
# Doorkeeper::Application.find_by!(name: 'Local Sinatra Application').uid
APP_ID = ENV['SALSIFY_APPLICATION_ID'].freeze
# OAuth application secret. Can be obtained from production Rails console via:
# Doorkeeper::Application.find_by(name: 'Local Sinatra Application').secret
# WARNING: Do NOT store the secret in version-controlled source code.
# Use environment variables or another mechanism to feed this information to the application.
SECRET = ENV['SALSIFY_APPLICATION_SECRET'].freeze
 
# Change this if you're running against a local instance of Dandelion.
# DANDELION = 'http://localhost:5000'.freeze
DANDELION = 'https://app.salsify.com'.freeze
 
UNAUTHENTICATED_PATHS = ['/auth/callback'].freeze
 
# Configure the OAuth2 client for later use
require 'oauth2'
set :oauth_client, OAuth2::Client.new(APP_ID, SECRET, site: DANDELION)
 
# This application uses curl to retrieve data from Dandelion, but any HTTP client that allows
# you to set custom headers is sufficient.
require 'curl' # comes from the "curb" gem
 
# The before block runs before each request is processed.
before do
 # The current token is serialized into a cookie, re-materialze it for use.
 if session.has_key?(:current_token)
 @current_token = OAuth2::AccessToken.from_hash(settings.oauth_client, session[:current_token])
 end
 
 # If there is no current token in the user's session, redirect immediately to the sign in page.
 # If only part of your application requires authorized access, you should limit this behavior.
 unless @current_token || UNAUTHENTICATED_PATHS.include?(request.path)
 redirect_to_sign_in_page!
 end
end
 
# The after block runs after each request is processed.
after do
 # Serialize the current token object back into the user's cookie
 if @current_token
 session[:current_token] = @current_token.to_hash
 end
end
 
# Get some data via the API and feed it to the information-hungry consumer.
get '/' do
 response = with_auth_token_refresh do
 curl_client = Curl::Easy.new
 curl_client.verbose = true # handy for debugging, e.g. making sure the correct headers are being sent
 curl_client.url = DANDELION + '/api/v1/products/100'
 # This is the important part. All of your requests to the API need this header.
 # Authorization: Bearer 0123456789abcdef
 curl_client.headers['Authorization'] = "Bearer #{@current_token.token}"
 curl_client.perform
 curl_client
 end
 
 # Catch all for handling a 401 response code
 # (e.g. if we weren't given a refresh token)
 if response.response_code == 401
 redirect_to_sign_in_page!
 end
 
 [response.response_code, {'Content-Type' => response.content_type}, response.body_str]
end
 
# This is the callback that Dandelion, the provider, will send the client to when authorization
# succeeds. We only care about the access token and the user's original destination for now.
get '/auth/callback' do
 @current_token = settings.oauth_client.auth_code.get_token(params[:code], redirect_uri: CALLBACK)
 redirect to(session.delete(:post_auth_redirect))
end
 
helpers do
 # Interprets the return code of a request. If the request
 # is unauthorized, attempt to use the refresh token and request again.
 def with_auth_token_refresh
 response = yield
 
 if response.response_code == 401 && @current_token.refresh_token
 begin
 @current_token = @current_token.refresh!
 rescue OAuth2::Error
 # If the token refresh fails, take the user
 # back to the sign in page
 redirect_to_sign_in_page!
 end
 response = yield
 end
 
 response
 end
 
 # Immediately redirect the user to the auth provider's sign in page.
 def redirect_to_sign_in_page!
 session[:post_auth_redirect] = request.path
 redirect to(settings.oauth_client.auth_code.authorize_url(redirect_uri: CALLBACK))
 end
end