Skip to content

@xid-kit/tauri

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

Status

Registry status: UNPUBLISHED. Install this SDK only from the repository source checkout; do not use an external package registry.

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.
Navigation

Type to search...

Use arrow keys to navigateEnter to selectEscape to close