Integrating your Rails application with Okta

Okta is an enterprise-grade, identity management service, built for the cloud, but compatible with many on-premises applications. It has multiple products related to identity management.

Today we’ll be integrating Okta with a Ruby on Rails application.

Warning: This blog won’t cover Okta integration as an authentication service to a Rails application. It will, however, cover how to connect to an Okta organization and access its data through Okta API.

You can either create a trial account or a developer account but we’ll be using a developer account as it’s better suited for testing and development purposes.

Use this link to register for a developer account https://developer.okta.com/signup/.

Refer to this link for the developer documentation https://developer.okta.com/docs/.

And use this link to play with the APIs using Postman https://developer.okta.com/code/rest/.

After successfully registering for a developer account you should be able to login to Okta using your exclusive URL which should look something like https://dev-####.okta.com. Visit API > Tokens in the top navigation menu. Click on the Create Token button. Enter a name for this token and click on the Create Token button. Copy the token value. Make sure you keep the token somewhere safe as this will be the only time you’d be able to view it. If you lose it you’ll have to create and use a new one.

This blog won’t walk you through the steps to create a new Rails application. It’s assumed that you can create a new Rails application.

The whole Rails application that we’re gonna be creating in this article is available at GitHub: https://github.com/sarmad90/okta_rails_integration.

Edit your Gemfile and add a new gem “oktakit” and run bundle install.

gem “oktakit”

Open up your Rails console by rails console command and follow the instructions of Oktakit gem.

token = “token”
organization = “dev-207260”
client = Oktakit.new(token: token, organization: organization)
response, http_status = client.list_users

This is how the response should look.

response will contain a collection of users from Okta.

You can also try any APIs using this API key using Postman if you follow the instructions in the link mentioned above.

But there are some considerations with this flow. This token has all the permissions of the account which was used to create this token. So for example, if an admin user creates this token, it will have all the permissions as that admin. Which may not be ideal in some use cases where it’s not desirable or safe to grant excessive permissions.

OAuth 2.0 is another flow that might be more ideal for other use cases. One advantage is that we have more control over the level of permissions and the other is that we just have to set it up once and then we can authenticate any account within an organization, unlike API key flow where we would need to ask every user which needs API access to generate an API key and pass it to the application manually.

Let’s move on to OAuth 2.0 flow without any further delay. First of all, we need to create an application inside the organization we want to authenticate. We’ll go with the Web type application in this article.

Testing OAuth 2.0 authentication flow requires a functional callback URL. We’ll use ngrok to expose our localhost application to the internet through HTTP tunneling. You can read more about ngrok https://ngrok.com. Once you have ngrok installed, run ngrok on your port 3000 with this command: ./ngrok http 3000 (replace ./ with your path to ngrok executable).

Configure your Okta OAuth 2.0 application setting your ngrok URI as the base URI and <your-ngrok-host>/oauth/okta_callback as Login redirect URI. Make sure Everyone is selected in the Group assignments so that all users in the organization can authenticate. At last, make sure the refresh token checkbox is checked. Access tokens that are obtained after successful authentication usually expire after one hour. This refresh token will enable us to refresh the access token without the need to re-authenticate every time the token expires.

After completing the configuration, grab the Client ID and Client secret which we’ll need to set in our Rails application. 

If you’re using Rails  version 5.2 or above then it’s advisable to store the Client secret in credentials.yml.enc file, otherwise somewhere that’s not checked in the version control system. Maybe in an environment variable or something like that which does not expose this sensitive piece of information unncessarily.

Then visit the Okta API Scopes tab and grant the okta.users.read scope to your application.

Now let’s move on to changes in our Rails application code.

Edit your Gemfile and add another new gem “signet” and run bundle install.

Signet is a gem with OAuth 1.0 and 2.0 implementation in Ruby. It will help us connect to our Okta account using OAuth 2.0.

Create a new Rails model to hold authentication information and access tokens etc.

Use this generator command to create the model and DB migration:

rails generate model okta_authentication account_name data:jsonb

And then run rails db:migrate to run the migration and create the DB table.

Create an initializer to require Signet’s OAuth client and set Okta client secret in Rails application config.

# config/initializers/okta.rb
require ‘signet/oauth_2/client’

Rails.application.configure do
config.okta = {
  client_secret: Rails.application.credentials.okta[:client_secret],
}
end

This sets Okta application’s client secret to Rails config. Accessing Rails credentials directly in the code isn’t effective as they need to be decrypted everytime they are accessed.

Add a service for Okta authentication in app/services. You can place this service in the lib folder too if you like. I like to keep it in app/services.

# app/services/okta/authenticator.rb
module Okta
class Authenticator
  def authentication_url
    auth_client.authorization_uri.to_s
  end

  def receive_authorization_code(code)
    auth_client.code = code
    auth_client.fetch_access_token!

    # save access_token_object in DB
    OktaAuthentication.create!(
      account_name: “dev-207260”,
      data: {
        auth_object: JSON.parse(auth_client.to_json),
      }
    )
  end

  def auth_client
    @auth_client ||= Signet::OAuth2::Client.new(
      authorization_uri: “https://dev-207260.okta.com/oauth2/v1/authorize”,
      token_credential_uri: “https://dev-207260.okta.com/oauth2/v1/token”,
      client_id: “0oaaksj7aDNjG19yZ4x6”,
      client_secret: Rails.application.config.okta[:client_secret],
      response_type: “code”,
      redirect_uri: “https://d2cd1921.ngrok.io/oauth/okta_callback”,
      state: “state-296bc9a0-a2a2-4a57-be1a-d0e2fd9bb601”,
      grant_type: “authorization_code”,
      scope: [
        “openid”,
        “offline_access”,
        “profile”,
        “email”,
        “address”,
        “phone”,
        “okta.users.read”,
      ]
    )
  end
end
end

Okta:Authenticator class is responsible for two things primarily.

  1. Generating appropriate Okta authentication URL.
  2. Processing the received Okta authorization code after successful authentication and to obtain an access token against the authorization code.

We’re using Signet library to instantiate the auth client by providing all required information. Everything else is pretty self explanatory.

Generate a Rails controller named OAuthController with two actions okta_callback and authentiacte_okta. Routes will also be added by this command.

rails generate controller oauth okta_callback authenticate_okta

okta_callback will be responsible for receiving the authorization code in a callback from Okta and then obtaining the access token and saving it to DB. authentiacte_okta will be responsible for generating and redirecting the user to Okta authentication URL.

# app/controllers/oauth_controller.rb
class OauthController < ApplicationController
def okta_callback
  okta_authenticator.receive_authorization_code(params[:code])
end

def authenticate_okta
  redirect_to okta_authenticator.authentication_url
end

private

def okta_authenticator
  @okta_authenticator ||= Okta::Authenticator.new
end
end

The code is simple and the intent is clear. Thus no explanation needed.

Fancy taking your application on a spin? Let’s go.

Visit localhost:3000/authenticate_okta in your browser. If everything is set up correctly you’ll be redirected to Okta authentication URL and will be redirected back to your application on successful authentication via ngrok host, not the localhost. At this point you should have an OktaAuthentication record in the DB with the access token.

If you followed the link to set up the Postman collection of Okta APIs then go to Postman to try your access token to make API calls. Let’s try List Users API.

Make sure to disable the Authorization header which uses the API key authentication method. We want to test our OAuth 2.0 access token.

Head over to the Authorization tab and select OAuth 2.0 in the type dropdown and paste the the access token you grabbed in the access token field. Click Send to make the call and you should be able to see the response. Note: In case the access token is expired it can be manually refreshed using refresh! method on the signet auth client instance.

Don’t forget, if you’re having any problems following this article the whole code solution lives in https://github.com/sarmad90/okta_rails_integration.

Unfortunately Oktakit gem does not support OAuth 2.0 access token flow currently. I’m planning to create a PR with a change to add the ability to use OAuth 2.0 access token instead of only the API key. But it should be pretty straightforward to do for any of you.

UPDATE: I actually managed to create the PR to add the ability to use OAuth 2.0 access token instead of only the API key. Here’s the PR: https://github.com/Shopify/oktakit/pull/30. And here’s the link to my fork https://github.com/sarmad90/oktakit while I wait for the PR to be accepted and merged into a base repo. If your Okta gem is using my fork in the Gemfile through passing my git repo URL, you can do something like this to instantiate the Okta client, similar to what we did earlier in this article, to use the access token for authentication.

client = Oktakit.new(access_token: valid_access_token, organization: organization)

And you’ll be good to go to use all the APIs just like you could use them with the API token.

You have a good basis to start and can now go on your merry way with exploring the Okta APIs and integration further.