How to build SAML SSO with WorkOS, Okta, and Go - Featured Image
Web development7 min read

How to build SAML SSO with WorkOS, Okta, and Go

When you're creating a website or app for business clients, adding Single Sign-On (SSO) support becomes really important. It makes things easier for users and meets enterprise security requirements.

This guide will show you how to set up SAML SSO using Okta as your identity provider, Go for the backend code, and WorkOS to handle the authentication process.

We'll cover two different ways to do this: using AuthKit (which gives you a ready-made login interface) or building your own custom login form.

Keep in mind that software tools change frequently, so if something doesn't match exactly what you see, check the official documentation for the latest updates:

Prerequisites

Before we start, make sure you have:

  • An Okta account with admin access

  • A WorkOS account

  • A Go application (version 1.13 or newer)

Step 1: Install the SDK

First, add the WorkOS Go SDK to your project:

go get -u github.com/workos/workos-go/...

Step 2: Set secrets

To connect with WorkOS, you need your API key and client ID. You can find these in your WorkOS dashboard.

(Source: Workos)

Store these values securely and make them available to your app through environment variables:

WORKOS_API_KEY='sk_example_123456789'
WORKOS_CLIENT_ID='client_123456789'

Make sure to follow security best practices when handling these sensitive credentials.

Step 3: Configure the Okta connection

In WorkOS, SSO connections work at the organization level. An organization groups users together and controls which enterprise features they can use. When you enable SSO for an organization, all users in that group can use it.

This setup lets you do things like automatically requiring users with certain email domains to use specific SSO providers.

WorkOS configuration

  1. Go to your WorkOS dashboard and click on Organizations in the sidebar

  2. Choose an existing organization or create a new one for your Okta setup

  3. Find the "Single Sign-On" section and click "Configure manually"

  4. Select "Okta" from the dropdown and click "Create Connection"

(Source: Workos)

You'll need three pieces of information to set this up:

  • ACS URL: This is where Okta will send authentication responses after users log in

  • SP Entity ID: A unique identifier that tells Okta who is making the authentication request

  • IdP Metadata URL: Contains all the configuration details from your Okta setup

WorkOS provides the first two values automatically. You'll see them in the "Service Provider Details" section right after creating the connection.

(Source: Workos)

Okta configuration

Now let's set things up on the Okta side:

(Source: Workos)

  1. Log into your Okta admin dashboard

  2. Go to "Applications" and either select your existing app or create a new one

  3. Enter the ACS URL from WorkOS as the "Single Sign-On URL"

  4. Enter the SP Entity ID as the "Audience URI (SP Entity ID)"

  5. In the "Attribute Statements" section, add these mappings:

    • id → user.id

    • email → user.email

    • firstName → user.firstName

    • lastName → user.lastName

  6. Assign users or groups to this application

  7. Go to the "Sign On" tab, click "Actions" for your certificate, and select "View IdP Metadata"

  8. Copy the URL from the new tab that opens

  9. Back in WorkOS, click "Edit Configuration" in the "Identity Provider Configuration" section

  10. Paste the Metadata URL and click "Save Metadata Configuration"

Your connection should now be active and ready to use.

Step 4: Configure a redirect URI

A redirect URI is where users go after they successfully sign in. We'll build this endpoint shortly, but first we need to register it in WorkOS.

Go to the Redirects section in your WorkOS dashboard and add your callback URL.

(Source: Workos)

Note that while you can use wildcards during development, production environments require exact URLs without wildcards or query parameters.

Step 5: Set up the frontend

Let's create a simple frontend interface. This example uses React, but you can use any framework you prefer.

If you don't have a React app yet, create one and add this code to your App.js:

export default function App() {
  return (
    <div className="App">
      <h1>SSO example</h1>
      <p>
        <a href="/login">Sign in</a>
      </p>
      <p>
        <a href="/logout">Sign out</a>
      </p>
    </div>
  );
}

Step 6: Set up the backend

(Source: Workos)

The authentication process happens in two main steps:

  1. Start authentication: Redirect the user to their identity provider (Okta)

  2. Complete authentication: Handle the response and get the user's information

You can choose between two approaches:

  • AuthKit: Use WorkOS's complete User Management API with built-in SSO support

  • Custom login: Build your own interface using the standalone SSO API

SSO with AuthKit

Initiate login

When someone clicks "Sign in", we need to start the authentication process. We'll use the usermanagement.GetAuthorizationURL method to create the URL where users will authenticate.

Add this code to your main.go:

package main

import (
    "context"
    "net/http"
    "os"

    "github.com/workos/workos-go/v2/pkg/usermanagement"
)

func main() {
    apiKey := os.Getenv("WORKOS_API_KEY")
    clientID := os.Getenv("WORKOS_CLIENT_ID")

    usermanagement.SetAPIKey(apiKey)

    redirectURI := "https://your-app.com/callback"
    provider := "authkit"

    http.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) {
        authorizationUrl, err := usermanagement.GetAuthorizationURL(
            context.Background(),
            usermanagement.GetAuthorizationURLOpts{
                ClientID:     clientID,
                Provider:     provider,
                RedirectURI:  redirectURI,
                State:        "dj1kUXc0dzlXZ1hjUQ==",
            },
        )

        if err != nil {
            // Handle error appropriately
            return
        }

        http.Redirect(w, r, authorizationUrl.String(), http.StatusSeeOther)
    })
}

After authentication, WorkOS redirects users back to your app with an authorization code in the URL, like this:

https://your-app.com/callback?code=g0FGFmNjVmOWIkTGf2PLk4FTYyFGU5

Handle the callback

Next, we need to exchange that authorization code for user information and access tokens.

Add this callback handler to main.go:

http.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) {
    opts := usermanagement.AuthenticateWithCodeOpts{
        ClientID: clientID,
        Code:     r.URL.Query().Get("code"),
    }

    profileAndToken, err := usermanagement.AuthenticateWithCode(r.Context(), opts)
    if err != nil {
        // Handle the error properly
        return
    }

    userProfile := profileAndToken.User
    accessToken := profileAndToken.AccessToken

    // Use this information for your business logic
    
    http.Redirect(w, r, "/", http.StatusSeeOther)
})

The successful response includes detailed user information and tokens for session management:

{
  "user": {
    "object": "user",
    "id": "user_01E4ZCR3C56J083X43JQXF3JK5",
    "email": "user@example.com",
    "first_name": "John",
    "last_name": "Doe",
    "email_verified": true,
    "created_at": "2021-06-25T19:07:33.155Z",
    "updated_at": "2021-06-25T19:07:33.155Z"
  },
  "organization_id": "org_01H945H0YD4F97JN9MATX7BYAG",
  "access_token": "eyJhb.nNzb19vaWRjX2tleV8.lc5Uk4yWVk5In0",
  "refresh_token": "yAjhKk123NLIjdrBdGZPf8pLIDvK"
}

SSO without AuthKit

If you prefer to build your own login interface, use the standalone SSO API instead.

Initiate login

package main

import (
    "net/http"
    "os"

    "github.com/workos/workos-go/v3/pkg/sso"
)

func main() {
    apiKey := os.Getenv("WORKOS_API_KEY")
    clientID := os.Getenv("WORKOS_CLIENT_ID")

    sso.Configure(apiKey, clientID)

    orgID := "org_test_idp" // Use your actual organization ID
    redirectURI := "https://your-app.com/callback"

    http.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) {
        opts := sso.GetAuthorizationURLOpts{
            Organization: orgID,
            RedirectURI:  redirectURI,
        }

        authorizationUrl, err := sso.GetAuthorizationURL(opts)
        if err != nil {
            // Handle error
            return
        }

        http.Redirect(w, r, authorizationUrl.String(), http.StatusSeeOther)
    })
}

Handle the callback

http.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) {
    opts := sso.GetProfileAndTokenOpts{
        Code: r.URL.Query().Get("code"),
    }

    profileAndToken, err := sso.GetProfileAndToken(context.Background(), opts)
    if err != nil {
        // Handle error
        return
    }

    profile := profileAndToken.Profile
    
    // Use profile information for your app logic

    http.Redirect(w, r, "/", http.StatusSeeOther)
})

This approach gives you a response focused on the authentication profile:

{
  "access_token": "01DMEK0J53CVMC32CK5SE0KZ8Q",
  "profile": {
    "object": "profile",
    "id": "prof_01DMC79VCBZ0NY2099737PSVF1",
    "connection_id": "conn_01E4ZCR3C56J083X43JQXF3JK5",
    "connection_type": "OktaSAML",
    "organization_id": "org_01EHWNCE74X7JSDV0X3SZ3KJNY",
    "email": "user@example.com",
    "first_name": "John",
    "last_name": "Doe"
  }
}

Step 7: Test the connection

(Source: Workos)

WorkOS provides a handy testing environment to make sure everything works correctly. Go to the Test SSO page in your WorkOS dashboard.

You can test several scenarios:

  • Service provider-initiated SSO: Users start from your login page and get redirected to Okta

  • Identity provider-initiated SSO: Users start from Okta and get redirected to your app

  • Error handling: Test how your app handles authentication failures

Follow the on-screen instructions to run through these test cases and verify your integration works properly.

Next steps

Congratulations! You now have a working SAML SSO integration. But this is just the beginning. Here are some important features to consider adding next:

  • Secure session management and token handling

  • Logout functionality

  • Social login options (Google, GitHub, etc.)

  • Role-based access control

  • Automatic user provisioning

  • Error handling and logging

  • Multi-factor authentication support

Each of these features will make your authentication system more robust and user-friendly. Take them one step at a time based on your application's specific needs.

Posted on: 23/6/2025

Posted by





Subscribe to our newsletter

Join 2,000+ subscribers

Stay in the loop with everything you need to know.

We care about your data in our privacy policy

Background shadow leftBackground shadow right

Have something to share?

Write on the platform and dummy copy content

Be Part of Something Big

Shifters, a developer-first community platform, is launching soon with all the features. Don't miss out on day one access. Join the waitlist: