If you’re building a SaaS product, it’s likely that you'll consider implementing SSO (Single Sign-On) for your platform. There are many options to choose from such as Google, Apple, Facebook, Okta, and more. Depending on the size of your business and your customer needs you might want to support many of them. So how do you decide which is the right choice?
Okta is one of the more popular and widely adopted SSO providers. It provides benefits for end users and your business. For your end users, the Okta experience means there is no need to remember tons of passwords. One dashboard and a simple login flow allows access into different services. For your customers, this access flow is also useful - ensuring all permissions management is centralized in one place. Additionally, you get a simple user onboarding, and off-boarding process.
Okta appears and operates like a market with different apps. You can just select a defined app, get a set of secrets, exchange it with a target service (AWS, SalesForce, Zoom, etc.) and that's it!
For us, at Sensor Tower, we want to be part of that ecosystem, where we streamline and simplify how our customers login to the Sensor Tower platform. The approach we decided to go with was to build our own Okta app and publish it in OIN (Okta Integration Network). We share some technical and organizational aspects of that process in the section below.
It's also worth mentioning that we decided to support only OAuth2, not SAML. SAML is an old, heavy, and widely known standard for authentication and authorization flow, but we wanted to keep integration simple and lightweight. That is why OAuth was chosen. And, since Okta is a mediator between us and our clients, we don't need to worry about compatibility with the end client systems. Within auth flow there is no direct communication between our platform and any client services. So, we're free to select any auth protocol.
Usually, this is the first question which comes to mind of any developer. Because Google SSO is a very popular tool. With Google, you get one button "Login with Google", easy OAuth2 flow, and secure authentication. At a first glance Okta provides the same functionality, but this is a deceptive feeling especially in technical and integration areas.
Google flow is extremely simple and well documented. Go to the Google Cloud console, create OAuth2 client credentials, use secrets in your app, and that's it! Even if you want to setup a custom OAuth2 integration and require sensitive/restricted scopes; you more-less know the flow. On each step you'll receive a standard email with the current status of your integration along with issues which should be solved. Moreover, almost zero communication with any live person is needed. There are also plenty of answers on StackOverflow if the email content is not obvious for you.
Okta is a completely opposite scenario. There is a lot of documentation for app users, but not for app developers. There are several unclear technical things which you should be aware of for a successful integration. You should also expect to have email interaction with Okta staff through the app publishing and verification process.
In the case of Google OAuth you have a “client_id + client_secret” pair. Usually devs hardcode it (or put it env or whatever else place), and then use it within the app initialization process. It's straightforward and simple.
In the case of Okta, each installed app has its own client_id+client_secret. For example, each time an organization installs your app from OIN into its own account, Okta generates a new set of secrets. This is not totally obvious, but a very important thing. This fact makes it impossible to use standard libs/gems like omniauth + omniauth-okta. and happy path from the guide to handle that scenario". You'll need to write some custom code.
As you probably know, at Sensor Tower we use Ruby. Under the hood we have combined it with omniauth gems. Naturally, we first thought about using omniauth-okta for Okta integration but because for Okta each app installation has its own secrets, we can't just pass client_id+client_secret into the strategy initializer. In practice it means that each auth request might require a different set of secrets.
To handle this, we have to go deeper and look at omniauth together with the OAuth2 protocol. There is an interesting thing: configuring setup within omniauth strategy initializer:
Technically, all that onmiauth magic is a Rack middleware. So, we can hook into a specific place with each auth request, do some preparations and continue the flow with the desired auth settings.
Hence, within OKTA_SETUP_PROC we have to make a decision which pair of client_id+client_secret we should use and proceed with it. This sounds simple, but the devil is in the details.
First of all, OKTA_SETUP_PROC receives an env. Which might be converted into request by request = Rack::Request.new(env). At this point we can access params and read anything in a comprehensive way.
If somebody clicks on the icon of our app on the Okta dashboard, in the first auth request we can find iss arg. This is the domain of our Okta account (e.g. https://sensortower.okta.com/.)
Based on that domain we can select a secret pair and put it into env["omniauth.strategy"].options.deep_merge!(omniauth_settings) where omniauth_settings contains slightly more than just two secrets:
OAuth2 flow requires more than one request and there is no iss arg in the second request params but there is state. In general, it contains SecureRandom.hex(24), but we can set anything there within the first request. For instance, there might be some encrypted data, or uuid of an internal entity, or anything else which you want to pass between auth requests. So, we decided to put a unique random string and store all data in Redis with the same key:
Here SHORT_TTL might be a couple dozens of seconds because, by security reasons, internally OAuth2 has a really short TTL of intermediate auth content.
In the first request we have iss, in the second - state. Hence, in both cases we can determine the set of secrets for the auth.
Hint: Add some logging / debug information inside the OKTA_SETUP_PROC block to track all anomalies, such as the inability to determine a secret pair. This is helpful, since there’s no easy way to reproduce the behavior, especially in a production environment.
This is the end of the technical part but not the end of the whole story. Because according to Brooks coding requires only 1/6 from overall time.
First, we have to go to the OIN manager and submit the form with details about our new Okta app. It's better to prepare all URLs and paths up front, because this is really painful to change within the verification process. The main pitfalls here are description and icon. This is not an obvious thing for developers and it is better to find a friend from the Marketing department.
Next, Okta representatives will reach out to you over email, and your app verifications process begins. Besides verifying all the data that you have already submitted in the form, you’ll need to clarify other questions about integration. Plus, according to this checklist you'll have to set up a full production-like environment to test the app against. You can either use a publicly available staging server, or implement a feature-flag together with the list of allowed SSO users for checking the flow on production.
The Okta staff will then want to verify all parts of the app to be sure it is working correctly. The main reason for this: after the app is published, its configuration will be frozen and there will be no way to change it without a full resubmission process. Technically, resubmission (or an update of the app) looks the same as the initial verification process. It requires the same form, same emails, same requirement of production-like environment etc. So, be very careful with all fields in the OIN manager form.
After QA tests are completed successfully, we have to prepare a customer-facing configuration guide. This is another piece which is usually difficult for developers. For this step you need to communicate with your Marketing / Product departments to build a good, user-friendly manual. That manual should be posted somewhere publicly. For example, in your corporate blog or help page - here's ours. If you have no blog or similar place, then you can place a PDF-file with the documentation into your project and allow it to be downloaded without authentication. A link to the document will be incorporated as a part of the Okta app, together with other settings like URLs, description, etc.
At this point, everything is ready for a final review and publishing. This last step also requires a couple more emails to/from Okta. And that's it! Now you have your app in OIN and anybody with an Okta account can use it.
It’s likely that someone will ask you about the installation of multiple apps within the same Okta account. In that case iss is not enough for determining a set of secrets for a success auth process. You'll need to construct a dynamic Initiate Login URI, to add some unique parameters that make a distinction between the same apps within a single account. Next, you'll need to initiate the resubmission process, update the documentation, and mention the new app parameter, along with its use cases. This is another story, for another time.