defmodule WorkOS.SSO do @moduledoc """ Manage Single Sign-On (SSO) in WorkOS. @see https://docs.workos.com/sso/overview """ require Logger alias WorkOS.Empty alias WorkOS.SSO.Connection alias WorkOS.SSO.Profile alias WorkOS.SSO.ProfileAndToken @doc """ Lists all connections. Parameter options: * `:connection_type` - Filter Connections by their type. * `:organization_id` - Filter Connections by their associated organization. * `:domain` - Filter Connections by their associated domain. * `:limit` - Maximum number of records to return. Accepts values between 1 and 100. Default is 10. * `:after` - Pagination cursor to receive records after a provided event ID. * `:before` - An object ID that defines your place in the list. When the ID is not present, you are at the end of the list. * `:order` - Order the results by the creation time. Supported values are "asc" and "desc" for showing older and newer records first respectively. """ @spec list_connections(WorkOS.Client.t(), map()) :: WorkOS.Client.response(WorkOS.List.t(Connection.t())) def list_connections(client, opts) do WorkOS.Client.get(client, WorkOS.List.of(Connection), "/connections", query: [ connection_type: opts[:connection_type], organization_id: opts[:organization_id], domain: opts[:domain], limit: opts[:limit], after: opts[:after], before: opts[:before], order: opts[:order] ] ) end @spec list_connections(map()) :: WorkOS.Client.response(WorkOS.List.t(Connection.t())) def list_connections(opts \\ %{}) do WorkOS.Client.get(WorkOS.client(), WorkOS.List.of(Connection), "/connections", query: [ connection_type: opts[:connection_type], organization_id: opts[:organization_id], domain: opts[:domain], limit: opts[:limit], after: opts[:after], before: opts[:before], order: opts[:order] ] ) end @doc """ Deletes a connection. """ @spec delete_connection(String.t()) :: WorkOS.Client.response(nil) @spec delete_connection(WorkOS.Client.t(), String.t()) :: WorkOS.Client.response(nil) def delete_connection(client \\ WorkOS.client(), connection_id) do WorkOS.Client.delete(client, Empty, "/connections/:id", %{}, opts: [ path_params: [id: connection_id] ] ) end @doc """ Gets a connection given an ID. """ @spec get_connection(String.t()) :: WorkOS.Client.response(Connection.t()) @spec get_connection(WorkOS.Client.t(), String.t()) :: WorkOS.Client.response(Connection.t()) def get_connection(client \\ WorkOS.client(), connection_id) do WorkOS.Client.get(client, Connection, "/connections/:id", opts: [ path_params: [id: connection_id] ] ) end @doc """ Generates an OAuth 2.0 authorization URL. Parameter options: * `:organization` - The organization connection selector is used to initiate SSO for an Organization. * `:connection` - The connection connection selector is used to initiate SSO for a Connection. * `:redirect_uri` - A Redirect URI to return an authorized user to. (required) * `:client_id` - This value can be obtained from the SSO Configuration page in the WorkOS dashboard. * `:provider` - The provider connection selector is used to initiate SSO using an OAuth provider. * `:state` - An optional parameter that can be used to encode arbitrary information to help restore application state between redirects. * `:login_hint` - Can be used to pre-fill the username/email address field of the IdP sign-in page for the user, if you know their username ahead of time. * `:domain_hint` - Can be used to pre-fill the domain field when initiating authentication with Microsoft OAuth, or with a GoogleSAML connection type. """ @spec get_authorization_url(map()) :: {:ok, String.t()} | {:error, String.t()} def get_authorization_url(params) when is_map_key(params, :redirect_uri) and (is_map_key(params, :connection) or is_map_key(params, :organization) or is_map_key(params, :provider)) do client_id = params[:client_id] || WorkOS.client_id() || raise "Missing required `client_id` parameter." defaults = %{ client_id: client_id, response_type: "code" } query = defaults |> Map.merge(params) |> Map.take( [ :client_id, :redirect_uri, :connection, :organization, :provider, :state, :login_hint, :domain_hint ] ++ Map.keys(defaults) ) |> URI.encode_query() {:ok, "#{WorkOS.base_url()}/sso/authorize?#{query}"} end def get_authorization_url(_params), do: {:error, "Incomplete arguments. Need to specify either a 'connection', 'organization', or 'provider'."} @doc """ Gets an access token along with the user `Profile`. Parameter options: * `:code` - The authorization value which was passed back as a query parameter in the callback to the Redirect URI. (required) """ @spec get_profile_and_token(String.t()) :: WorkOS.Client.response(ProfileAndToken.t()) @spec get_profile_and_token(WorkOS.Client.t(), String.t()) :: WorkOS.Client.response(ProfileAndToken.t()) def get_profile_and_token(client \\ WorkOS.client(), code) do WorkOS.Client.post(client, ProfileAndToken, "/sso/token", %{ client_id: WorkOS.client_id(client), client_secret: WorkOS.api_key(client), grant_type: "authorization_code", code: code }) end @doc """ Gets a profile given an access token. """ @spec get_profile(String.t()) :: WorkOS.Client.response(Profile.t()) @spec get_profile(WorkOS.Client.t(), String.t()) :: WorkOS.Client.response(Profile.t()) def get_profile(client \\ WorkOS.client(), access_token) do WorkOS.Client.get(client, Profile, "/sso/profile", opts: [ access_token: access_token ] ) end end