Sign-in with stacks - #70
Conversation
| | issued-at | (string-ascii 27) | The ISO 8601 datetime string of the current time. | | ||
| | expiration-time | (string-ascii 27) | (optional) The ISO 8601 datetime string that, if present, indicates when the signed authentication message is no longer valid. | | ||
| | not-before | (string-ascii 27) | (optional) The ISO 8601 datetime string that, if present, indicates when the signed authentication message will become valid. | | ||
| | request-id | (string-ascii 64) | an system-specific identifier that may be used to uniquely refer to the sign-in request. | |
There was a problem hiding this comment.
request-id should be optional too @friedger
|
|
||
| # Abstract | ||
|
|
||
| Web application often provide their services only to authenticated users. In |
|
|
||
| Web application often provide their services only to authenticated users. In | ||
| Web2, this was done through username and password or federated logins. In Web3, | ||
| users can proof their digital identity by cryptographically signing that the |
|
|
||
| | name | type | description | | ||
| | --------------- | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| | title | (string-ascii 126) | Must be the application's domain name (max 80) followed by ` wants you to sign in with your Stacks account` | |
There was a problem hiding this comment.
Can title just be the application name? Then, wants you to sign in with your Stacks account can be localized.
There was a problem hiding this comment.
if going with domain name, shouldn't title then follow domain name limitations, ie 253 characters in size. Also, why (string ascii 126) if max is 80?
| | title | (string-ascii 126) | Must be the application's domain name (max 80) followed by ` wants you to sign in with your Stacks account` | | ||
| | address | principal | The address of the signer | | ||
| | statement | (string-ascii 80) | (optional) Describes the terms and conditions the user agrees to by using the application. | | ||
| | URI | (string-ascii 80) | An RFC 3986 URI referring to the resource that is the subject of the signing (as in the subject of a claim). | |
There was a problem hiding this comment.
Is 80 characters enough for a URL? Shouldn't it be more like 4,096?
| | chain-id | uint | (optional) the chain ID to which the session is bound. This must correspond to the version of the address. | | ||
| | nonce | (string-ascii 64) | randomized token used to prevent replay attacks, at least 8 alphanumeric characters. | | ||
| | issued-at | (string-ascii 27) | The ISO 8601 datetime string of the current time. | | ||
| | expiration-time | (string-ascii 27) | (optional) The ISO 8601 datetime string that, if present, indicates when the signed authentication message is no longer valid. | |
There was a problem hiding this comment.
One of the tricky things with authentication tokens like this is making sure they can't be replayed by someone who captures them. Is there a way we can mandate expiration-time or perhaps use a timeout? Maybe we could use the Stacks blockchain height to determine how long a login token is good for (e.g. "This token expires at Stacks block height 12345").
|
I'd love to see this revived. Has anyone built any implementations of this? |
|
@obycode I have a working implementation for Sigle, if you are interested in using it I can publish it on npm. Can extract it to a smaller repo and publish it there https://github.com/sigle/sigle/tree/main/packages/sign-in-with-stacks |
|
Published it as a separate package on npm |
There was a problem hiding this comment.
Good ideas here: the EIP-4361 message grammar, the upstream replay/domain-binding security analysis, and near-zero developer re-education cost are all good reasons to base this on SIWE.
Align normatively with CAIP-122. CAIP-122 generalizes SIWE into a chain-agnostic data model, making EIP-4361 one namespace implementation of it rather than the root standard (Haardik & Ukustov, 2022; Chang, Rocco, Millegan, Johnson, & Terbu, 2021). A Stacks namespace profile already exists and specifies stacks:secp256k1 with SHA256 pre-hashing (Pradel, 2023). This SIP currently restates that profile instead of referencing it. Referencing it is leverage: Stacks inherits SIWX-capable adapters rather than needing bespoke per-app integration — siwx already lists Stacks as a supported namespace (LearnWeb3DAO, n.d.), and x402 ships CAIP-122 auth today (Coinbase Developer Platform, n.d.). Chain-agnostic auth plugins are also being requested upstream in Better Auth (better-auth, 2025).
App side stays as expected: generate and store a nonce, construct the message, call the wallet's sign-message RPC, verify server-side, bind to a session.
Wallet side is small but non-optional. No new RPC method is needed. The wallet must detect a SIWS-shaped payload inside the existing sign-message call, render a structured consent screen, and match the message's domain field against the actual request origin. That origin check is the entire security value over a raw message prompt and can only live in the wallet.
Suggested basis: SIP-030, not legacy Connect. Concretely: specify transport as stx_signMessage with messageType: "utf8"; state that stx_getAddresses supplies the address before message construction; drop any dependence on the legacy @stacks/auth JWT flow (janniks, 2024).
Remaining gaps
- No SIP-030 method binding is described.
- Verification does not specify deriving the Stacks address from the returned
publicKeyand comparing it to theaddressfield — without this a wallet can return a signature from a different key than the one claimed. - The 80-character URI and 10-resource limits are wallet display constraints in a wire format; move them to a SHOULD in wallet UX guidance.
- Presenting a Bitcoin address alongside the Stacks address in a single flow is unspecified, which is the practical blocker for dual-chain wallets.
I believe the Leather tracking issue (leather-io/mono#1832) was closed unintentionally; I will check with the team about reopening it.
References
better-auth. (2025). SIWX (Issue No. 6738). GitHub. better-auth/better-auth#6738
Chang, W., Rocco, G., Millegan, B., Johnson, N., & Terbu, O. (2021). ERC-4361: Sign-In with Ethereum (Ethereum Improvement Proposals No. 4361). https://eips.ethereum.org/EIPS/eip-4361
Coinbase Developer Platform. (n.d.). Sign-In-With-X (SIWX). x402 documentation. https://docs.x402.org/extensions/sign-in-with-x
Haardik, & Ukustov, S. (2022). CAIP-122: Sign in with X (SIWx) (Chain Agnostic Improvement Proposals No. 122). https://standards.chainagnostic.org/CAIPs/caip-122
Aryzing, Janniks, Jamie, K. & Aboelenein, M. (2024). SIP-030: Wallet interface. #166
LearnWeb3DAO. (n.d.). siwx: Sign in with X [Computer software]. GitHub. https://github.com/LearnWeb3DAO/siwx
Pradel, L. (2023). Stacks namespace — SIWx [Draft]. Chain Agnostic Namespaces. https://namespaces.chainagnostic.org/stacks/caip122
|
What about implementing this in Wallets do not strictly need library support, A shared parser and verifier in Suggest implementing the CAIP-122 Stacks profile rather than SIWS-specific code. Same effort, and the serializer stays compatible with SIWX adapters. |
This PR defines a standard for message used for sign-in with stacks (#69)
It is mainly inspired by EIP-4361.
The standard requires the following limits that can be discussed: