Log in to your app

Applications on the Tailor Platform use both authentication and authorization:

  • Authentication: Handled by an external Identity Provider (Okta, Entra ID, Google Workspace) to verify user credentials.

  • Authorization: Enforced by the Tailor Auth service, which maps the authenticated identity to a valid TailorDB user and applies permissions.

After integrating your Identity Provider (IdP) with the Auth service, a user must exist in both the IdP and the Tailor Platform to successfully log in. This guide explains the concepts for:

  1. User Profile Management: How Tailor Platform connects authentication with TailorDB user profiles
  2. OAuth2 Client Configuration: How Tailor Platform's auth service acts as a standalone authentication service
  3. Login Process: Using OAuth2 flows with tailorctl

Authentication Architecture

Tailor Platform's auth service acts as a standalone authentication service that abstracts away the complexity of your Identity Provider. Instead of your applications directly integrating with Auth0, Okta, or other IdPs, they authenticate against Tailor Platform's unified OAuth2 interface.

Part 1: User Profile Management

Connecting Auth with TailorDB

Tailor Platform uses a User Profile Provider to connect authentication identities with your application's user data stored in TailorDB. This connection is configured through the tailor_auth_user_profile_config:

<span><span style="color: var(--shiki-token-function)">resource</span><span style="color: var(--shiki-color-text)"> &quot;tailor_auth_user_profile_config&quot; &quot;user&quot; {</span></span>
<span><span style="color: var(--shiki-color-text)">  workspace_id </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> var.workspace_id</span></span>
<span><span style="color: var(--shiki-color-text)">  namespace    </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> tailor_auth.ims_auth.namespace</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)">  tailordb_config </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">    namespace      </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> tailor_tailordb.ims.namespace</span></span>
<span><span style="color: var(--shiki-color-text)">    type           </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> tailor_tailordb_type.user.name</span></span>
<span><span style="color: var(--shiki-color-text)">    username_field </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&quot;email&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-comment)"># Attribute setting for Legacy Permission</span></span>
<span><span style="color: var(--shiki-color-text)">    attribute_fields </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> [</span></span>
<span><span style="color: var(--shiki-color-text)">      </span><span style="color: var(--shiki-token-string-expression)">&quot;roles&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">    ]</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-comment)"># Attribute setting for Permission/GQLPermission</span></span>
<span><span style="color: var(--shiki-color-text)">    attribute_map </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> [</span></span>
<span><span style="color: var(--shiki-color-text)">      roles </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&quot;roles&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">    ]</span></span>
<span><span style="color: var(--shiki-color-text)">  }</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>

Properties

PropertyDescription
workspace_idThe ID of the workspace that the user profile configuration belongs to.
namespaceThe auth namespace of the user profile configuration.
tailordb_configThe configuration for the TailorDB type
- namespaceThe namespace of the TailorDB.
- typeThe type of the TailorDB.
- username_fieldThe field that contains the username.
- attribute_fieldsList of attribute fields (applicable for legacy permissions only).
- attribute_mapMap of attributes.

With this attribute_map configuration, the Auth service fetches the roles field from the user type and assigns its value to the roles attribute for permission checks.

<span><span style="color: var(--shiki-token-keyword)">package</span><span style="color: var(--shiki-color-text)"> auth</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-token-keyword)">import</span><span style="color: var(--shiki-color-text)"> (</span></span>
<span><span style="color: var(--shiki-color-text)">  	</span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-string-expression)">github.com/tailor-platform/tailorctl/schema/v2/auth</span><span style="color: var(--shiki-color-text)">&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">  	</span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-string-expression)">tailor.build/sample/services/tailordb</span><span style="color: var(--shiki-color-text)">&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">  	</span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-string-expression)">tailor.build/sample/services/tailordb/type</span><span style="color: var(--shiki-color-text)">&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">  	</span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-string-expression)">tailor.build/sample/seed/master/data</span><span style="color: var(--shiki-color-text)">:roles</span><span style="color: var(--shiki-color-text)">&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">  )</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)">  oidc: auth.#Spec </span><span style="color: var(--shiki-token-keyword)">&amp;</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">  	Namespace: </span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-string-expression)">{AUTH_NAMESPACE}</span><span style="color: var(--shiki-color-text)">&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">  	...</span></span>
<span><span style="color: var(--shiki-color-text)">  	UserProfileProvider: auth.#UserProfileProviderType.TailorDB</span></span>
<span><span style="color: var(--shiki-color-text)">  	UserProfileProviderConfig: auth.#TailorDBProviderConfig </span><span style="color: var(--shiki-token-keyword)">&amp;</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">  		Namespace:     tailordb.Namespace</span></span>
<span><span style="color: var(--shiki-color-text)">  		</span></span>
<span><span style="color: var(--shiki-color-text)">  		# you can use choose the type of user profile</span></span>
<span><span style="color: var(--shiki-color-text)">  		Type:          type.User.Name</span></span>
<span><span style="color: var(--shiki-color-text)">  		UsernameField: </span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-string-expression)">email</span><span style="color: var(--shiki-color-text)">&quot;</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)">  		# you can use choose the user attribute fields</span></span>
<span><span style="color: var(--shiki-color-text)">  		AttributesFields: [</span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-string-expression)">roles</span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-color-text)">]</span></span>
<span><span style="color: var(--shiki-color-text)">  	}</span></span>
<span><span style="color: var(--shiki-color-text)">  ...</span></span>
<span><span style="color: var(--shiki-color-text)">  }</span></span>
<span></span>

This configuration tells Tailor Platform:

  • Where to find user data: The TailorDB namespace and type
  • How to match users: Using the email field as the username
  • What attributes to include: User roles for authorization

For more details on managing permissions, see the Permission Guide.

Creating Users in Your Application

Follow the Create Users Tutorial for a step-by-step guide.

Part 2: OAuth2 Client Configuration

Understanding OAuth2 in Tailor Platform

Tailor Platform's auth service acts as an OAuth2 Authorization Server that sits between your applications and your Identity Provider. This means:

  • Your applications authenticate against Tailor Platform's OAuth2 endpoints
  • Tailor Platform handles the complexity of integrating with your specific IdP (Auth0, Okta, etc.)
  • Users experience a seamless login flow regardless of the underlying IdP

This architecture allows you to:

  • Use standard OAuth2 flows in your applications
  • Switch Identity Providers without changing application code
  • Leverage Tailor Platform's user profile management and authorization

Configuring OAuth2 Clients

OAuth2 clients represent your applications that need to authenticate users. Each client has specific configuration for security and callback handling:

<span><span style="color: var(--shiki-token-function)">resource</span><span style="color: var(--shiki-color-text)"> &quot;tailor_auth_oauth2_client&quot; &quot;oauth2-client&quot; {</span></span>
<span><span style="color: var(--shiki-color-text)">  workspace_id </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> tailor_workspace.main.id</span></span>
<span><span style="color: var(--shiki-color-text)">  namespace    </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> tailor_auth.main_auth.namespace</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)">  name        </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&quot;oauth2-client&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">  description </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&quot;OAuth2 client for application access&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">  client_type </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&quot;confidential&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">  redirect_uris </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> [</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-string-expression)">&quot;http://tailorctl.tailor.tech:8086/callback&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">  ]</span></span>
<span><span style="color: var(--shiki-color-text)">  grant_types </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> [</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-string-expression)">&quot;authorization_code&quot;</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-string-expression)">&quot;refresh_token&quot;</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">  ]</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)">OAuth2Clients: [</span></span>
<span><span style="color: var(--shiki-color-text)">  auth.#OAuth2Client </span><span style="color: var(--shiki-token-keyword)">&amp;</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">    Name: </span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-string-expression)">oauth2-client</span><span style="color: var(--shiki-color-text)">&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">    Description: </span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-string-expression)">OAuth2 client for application access</span><span style="color: var(--shiki-color-text)">&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">    ClientType: auth.#OAuth2ClientType.Confidential</span></span>
<span><span style="color: var(--shiki-color-text)">    GrantTypes: [</span></span>
<span><span style="color: var(--shiki-color-text)">      </span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-string-expression)">authorization_code</span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">      </span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-string-expression)">refresh_token</span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">    ]</span></span>
<span><span style="color: var(--shiki-color-text)">    RedirectURIs: [</span></span>
<span><span style="color: var(--shiki-color-text)">      </span><span style="color: var(--shiki-color-text)">&quot;</span><span style="color: var(--shiki-token-string-expression)">http://tailorctl.tailor.tech:8086/callback</span><span style="color: var(--shiki-color-text)">&quot;</span></span>
<span><span style="color: var(--shiki-color-text)">    ]</span></span>
<span><span style="color: var(--shiki-color-text)">  }</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">]</span></span>
<span></span>

OAuth2 Client Properties

PropertyDescriptionSecurity Considerations
Name/nameUnique identifier for the OAuth2 clientUse descriptive names for easier management
ClientType/client_typeConfidential for server-side apps, Public for SPAsConfidential clients can securely store secrets
GrantTypes/grant_typesSupported OAuth2 flows: authorization_code, refresh_tokenAuthorization code is most secure for web apps
RedirectURIs/redirect_urisValid callback URLs after authenticationMust match exactly; wildcards not supported

Part 3: Login Process

Using tailorctl for Testing

The tailorctl command provides a convenient way to test your OAuth2 configuration:

<span><span style="color: var(--shiki-token-function)">tailorctl</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">workspace</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">app</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">login</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">-n</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">{APP_NAME}</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">-c</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">oauth2-client</span></span>
<span></span>

What happens during this flow:

  1. OAuth2 Authorization: TailorCTL initiates the OAuth2 authorization code flow
  2. IdP Redirect: Your browser opens to Tailor Platform's auth service, which redirects to your configured IdP
  3. User Authentication: You authenticate with your IdP (Auth0, Okta, etc.)
  4. Profile Lookup: Tailor Platform looks up your user profile in TailorDB using the email from your IdP
  5. Token Issuance: An access token is issued with your user's roles and permissions
  6. API Access: The token can be used to access your application's GraphQL API

Troubleshooting

User not found after authentication

  • Ensure the user exists in TailorDB with the correct email address
  • Verify the email matches exactly between IdP and TailorDB
  • Check that UserProfileProviderConfig is correctly configured

OAuth2 client errors

  • Verify redirect URIs match your application URLs exactly
  • Ensure client credentials are correctly configured
  • Confirm grant types include authorization_code
  • Check that the client type matches your application architecture

Permission denied

  • Confirm user has appropriate roles assigned in TailorDB
  • Verify TailorDB type permissions for the User model
  • Check that machine user has admin privileges for user creation
  • Ensure AttributesFields/attribute_map includes the roles field in your auth configuration

Role assignment issues

  • For UUID-based roles: Verify role UUIDs exist in your roles table
  • For string-based roles: Ensure role strings match exactly (case-sensitive)
  • Check that roles are properly seeded in your database

Next Steps