Skip to content
XID

@xid-kit/tauri

Tauri v2 desktop SDK with PKCE S256 flow, deeplink callback handler, OS keychain adapter, and Rust plugin template.

Status

XID publishes no packages to any registry. This SDK is distributed as source in the XID repository; use it from a checkout of the repository.

Package status is Current package. JS bridge, PKCE S256 flow, deeplink callback handler, OS keychain adapter, and Rust plugin template are implemented. A real IdP round-trip on production infrastructure is still pending manual verification.

Tauri configuration

// tauri.conf.json
{
  "bundle": { "identifier": "com.example.myapp" },
  "plugins": {
    "deep-link": { "desktop": { "schemes": ["myapp"] } }
  }
}

Rust plugin

Copy templates/xid-keychain-plugin.rs into src-tauri/src/xid_keychain.rs and register it following templates/tauri-app-setup.rs. Add keyring = "2", tauri-plugin-deep-link = "2", and tauri-plugin-shell = "2" to src-tauri/Cargo.toml.

JS integration

import { createXidTauriClient, createTauriKeychainAdapter } from '@xid-kit/tauri'
import { invoke } from '@tauri-apps/api/core'
import { open } from '@tauri-apps/plugin-shell'
import { onOpenUrl } from '@tauri-apps/plugin-deep-link'

const client = createXidTauriClient({
  issuer: 'https://xid.dev',
  clientId: 'YOUR_CLIENT_ID',
  redirectUri: 'myapp://auth/callback',
  keychain: createTauriKeychainAdapter({ invoke }),
})

// Register deeplink handler (e.g. on App component mount)
await onOpenUrl(async (urls) => {
  for (const url of urls) await client.handleRedirect(url)
})

// Trigger sign-in: opens system browser
await client.signIn({ openUrl: open })

Token retrieval and sign-out

// Get the current unexpired access token. Expiry requires a new sign-in.
const token = await client.getAccessToken()

// Get the current unexpired session (userId, organizationId, expiresAt).
const session = await client.getSession()

// Clear local keychain state. No refresh or revoke request is sent.
await client.signOut()

// To request full IdP sign-out, open an explicit OIDC RP-initiated logout URL.
const logoutUrl = client.buildSignOutUrl({ postLogoutRedirectUri: 'myapp://logout' })
await open(logoutUrl.toString())

Dev/test without Tauri runtime

import { createXidTauriClient, createMemoryKeychainAdapter } from '@xid-kit/tauri'

const client = createXidTauriClient({
  issuer: 'http://localhost:8788',
  clientId: 'test-client',
  redirectUri: 'http://localhost:1420/callback',
  keychain: createMemoryKeychainAdapter(),
})

createXidTauriClient options

Option Type Description
issuer string XID issuer URL
clientId string OAuth 2.0 client_id
redirectUri string Custom URI scheme callback
scopes readonly string[] Default: openid, profile, email
keychain XidKeychainAdapter Token storage adapter; default is MemoryKeychainAdapter (use Tauri adapter in production)

XidTauriClient methods

Method Description
signIn(options?) Build PKCE authorize URL; open via openUrl callback
handleRedirect(url) Parse deeplink, validate state, exchange code for tokens
getSession() TauriSession or null for a current unexpired local session; expired state is cleared
getAccessToken(options?) Current unexpired access token string or null; no refresh request is performed. The SDK rejects offline_access until DPoP is implemented.
signOut() Clear local keychain state without a revoke request
buildSignOutUrl(options?) Build OIDC end_session URL for RP-initiated logout
setTokenStorage(adapter) Swap keychain adapter at runtime

PKCE and token storage

  • PKCE S256 is always used. Plain challenge is never generated.
  • Verifier entropy is 64 bytes; challenge derived via Web Crypto crypto.subtle.digest('SHA-256', ...).
  • All keys are namespaced under xid.*. xid.access_token is current. xid.refresh_token is removed only as legacy cleanup. xid.session, xid.pkce_verifier, and xid.oauth_state hold current session and authorization state; no refresh credential is read or written.

Type to search

Use arrow keys to navigateEnter to selectEscape to close