Skip to main content

Crate stackure

Crate stackure 

Source
Expand description

Stackure is the Rust SDK for the Stackure authentication API.

Stackure provides passwordless B2B authentication. This SDK wraps the public API behind 10 free functions and a tower middleware.

§Quickstart

Protect an axum app:

let app = app.layer(stackure::auth());

The layer works in any tower stack: axum, tonic or hyper.

Access the authenticated user inside a handler:

let user = stackure::user_from_request(parts);

Manual verification without middleware:

let result = stackure::verify(parts).await;
if result.authenticated {
    // use result.user
}

Send a magic-link email:

let response = stackure::send_magic_link("user@example.com").await;

Sign the user out, ending their access everywhere. Mount it for every method on the sign-out path. Trigger it with a form or button that POSTs from the app’s own page; a link or any other request is sent to Stackure’s sign-out page, where the user confirms. Awaiting it yields the redirect response, never an error:

async fn logout(parts: http::request::Parts) -> axum::response::Response {
    stackure::logout(&parts).await
}

let app: Router = Router::new().route("/logout", any(logout));

§Sign-in handoff

Stackure’s session cookie is scoped to the Stackure host and is never visible to your app. After a successful magic-link sign-in, Stackure hands the browser back to the app’s registered URL with a session_token POST form field: an app-scoped session token valid only for this app, accepted by the validate endpoint for your app ID and never by Stackure itself.

The auth layer consumes it automatically: it validates the token, stores it in a cookie on your own domain and redirects to the same URL as a GET. Invalid tokens are ignored, and handoff bodies over 4 KB are ignored (treated as no token).

§Session binding

Sessions are not bound to the browser’s user agent or IP address. The SDK still forwards the original User-Agent and X-Forwarded-For on every validation call, but they are informational only; validation does not depend on them.

Every request with a session token is validated against Stackure, so revoking a session takes effect immediately. Requests without a well-formed token get the sign-in URL without a Stackure call, except under auth() on localhost, where the sign-in URL comes from Stackure.

§Content negotiation

The auth layer inspects the Accept header. Browser requests (Accept: text/html) redirect to the sign-in URL on 401. API requests (Accept: application/json) receive a JSON error body.

§MCP

Protect an MCP route:

let app = app.layer(stackure::mcp());

AI clients such as Claude, Claude Code, VS Code and Cursor sign users in through Stackure. This one line checks every MCP request in real time with the same app secret; there is no extra setup. The MCP endpoint must be served from the same site as the app’s registered URL unless an MCP URL is set for the app in Stackure.

The mcp layer reads only Authorization: Bearer and ignores cookies. It attaches the user exactly as auth does, and otherwise answers in JSON, never with a redirect or a cookie: 401 with a WWW-Authenticate header when not signed in, 503 when the check itself fails.

§Identity facts

Every authenticated User, from auth or mcp, also carries 2 facts from the user’s Stackure organization: user_is_app_admin is true when the user is an app admin or owner there, in charge of its apps, and user_teams lists the Stackure teams they belong to (empty when none). Stackure defines no in-app permissions; the app decides what these mean.

List the users in the caller’s organization who can open the app, and the teams that have it, for pickers and sharing. Each call returns one page, sorted by name:

let opts = stackure::DirectoryOptions {
    query: Some("ada".into()),
    ..Default::default()
};
let page = stackure::directory_users(parts, &opts).await?;
// page.items, page.next_cursor, page.total
let teams = stackure::directory_teams(parts, &Default::default()).await?;

query matches a user’s name, email and job title, or a team’s name. limit is the page size: 100 by default, 200 at most. Pass next_cursor as cursor to get the next page; it’s None on the last one. total is set on the first page when there are 10,000 matches or fewer. ids returns only those IDs that can open the app, up to 200, to check or name people and teams you already store.

directory_users and directory_teams are authenticated by the request’s session cookie, so call them from a route behind auth; MCP bearer tokens are not accepted. A request without a valid session gets StackureError::Auth.

§Configuration

STACKURE_APP_ID must be set to the app’s UUID, shown on the app page in Stackure, and STACKURE_APP_SECRET to the app secret shown when the app was registered (or last rotated) in Stackure. The secret is sent as the X-App-Secret header on every call except the sign-out made by logout, which carries the user’s session token instead. The first call that needs either fails with StackureError::Validation when it is unset, or when STACKURE_APP_ID is not a UUID.

STACKURE_BASE_URL must be set to your region’s address, https://eu.stackure.com or https://us.stackure.com. Every call goes there, and the first call fails with StackureError::Validation when it is unset.

People can open a newly registered app once an app admin gives it to everyone in the organization or to one of their teams in Stackure. App admins and the owner can always open it, so they can test sign-in first.

Every SDK call has one 2-second wall-clock deadline covering connect, headers, body and the single retry. The SDK retries once, after 500 ms, on a 5xx response or a connection failure, and only if more than 500 ms of the deadline remain. A timeout, including while reading the body, surfaces as StackureError::Timeout and is never retried. The localhost sign-in code swap is never retried, because each code works only once.

§Errors

Every function except verify and logout returns StackureError. logout never returns an error: a failed sign-out call still ends in a redirect. Match on the variant, or call StackureError::code for the same lowercase category string the other Stackure SDKs expose as .code.

Re-exports§

pub use errors::StackureError;
pub use middleware::Auth;
pub use middleware::AuthLayer;
pub use middleware::auth;
pub use middleware::logout;
pub use middleware::mcp;
pub use middleware::user_from_request;
pub use middleware::verify;
pub use types::DirectoryOptions;
pub use types::DirectoryPage;
pub use types::DirectoryUser;
pub use types::MagicLinkResponse;
pub use types::Session;
pub use types::Team;
pub use types::User;
pub use types::VerifyError;
pub use types::VerifyResult;

Modules§

errors
Stackure SDK error type.
middleware
Session verification and the tower authentication middleware.
types
Types returned by the Stackure SDK.
validation
Input validation for the Stackure SDK.

Constants§

SESSION_COOKIE
Name of the session cookie the SDK reads and writes.
TOKEN_PARAM
Name of the sign-in handoff parameter Stackure sends back.

Functions§

base_url
Resolve STACKURE_BASE_URL from the environment. Empty when unset.
directory_teams
List the teams in the caller’s Stackure organization that have the app, one page at a time: every team when the app is available to everyone, otherwise the teams it’s shared with.
directory_users
List the users in the caller’s Stackure organization who can open the app, one page at a time, for pickers and sharing.
send_magic_link
Send a passwordless sign-in email for the app in STACKURE_APP_ID.
validate_session
Validate the request’s session against Stackure.