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

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 current access token (refreshes transparently if near expiry)
const token = await client.getAccessToken()

// Get full session (userId, organizationId, expiresAt)
const session = await client.getSession()

// Revoke server session and clear all keychain entries
await client.signOut()

// OIDC RP-initiated logout URL for full IdP sign-out
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; refreshes token if near expiry
getAccessToken(options?) Access token string or null; refreshes if near expiry
signOut() Revoke server session and clear keychain
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, xid.refresh_token, xid.session, xid.pkce_verifier, xid.oauth_state.
Navigation

Type to search...

Use arrow keys to navigateEnter to selectEscape to close