Skip to content

Sign-in with stacks - #70

Open
friedger wants to merge 4 commits into
stacksgov:mainfrom
friedger:sign-in-with-stacks
Open

Sign-in with stacks#70
friedger wants to merge 4 commits into
stacksgov:mainfrom
friedger:sign-in-with-stacks

Conversation

@friedger

Copy link
Copy Markdown
Contributor

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:

  • URLs are limited to 80 ascii characters so that they can be shown on a normal screen.
  • 10 resources can be added to the message.

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

request-id should be optional too @friedger


# Abstract

Web application often provide their services only to authenticated users. In

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: "applications"


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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: "prove"

Comment thread sips/sip-x sign-in-with-stacks/sip-x-sign-in-with-stacks.md

| 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` |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can title just be the application name? Then, wants you to sign in with your Stacks account can be localized.

@dantrevino dantrevino Jun 2, 2022

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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").

@jcnelson jcnelson added the Draft SIP is in draft status label Jun 9, 2022
@obycode

obycode commented Apr 7, 2025

Copy link
Copy Markdown
Contributor

I'd love to see this revived. Has anyone built any implementations of this?

@pradel

pradel commented Apr 8, 2025

Copy link
Copy Markdown

@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

@pradel

pradel commented Jun 13, 2025

Copy link
Copy Markdown

Published it as a separate package on npm npm i sign-in-with-stacks and github at https://github.com/pradel/sign-in-with-stacks for anyone to use!

@314159265359879 314159265359879 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

  1. No SIP-030 method binding is described.
  2. Verification does not specify deriving the Stacks address from the returned publicKey and comparing it to the address field — without this a wallet can return a signature from a different key than the one claimed.
  3. 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.
  4. 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

@314159265359879

Copy link
Copy Markdown
Contributor

What about implementing this in @stacks/connect? @kyranjamie suggested as much in the Leather issue.

Wallets do not strictly need library support, stx_signMessage already carries the string. But without a canonical serializer, each wallet writes its own parser against the grammar, and the failure mode is silent divergence: wallet A recognizes the payload and shows the consent screen, wallet B does not and shows a raw prompt. Apps then feature-detect per wallet, which is the fragmentation the standard exists to prevent.

A shared parser and verifier in @stacks/connect, consumable by wallets as well as apps, as SIP-030 already anticipates for validation libraries, gives every implementation the same byte-for-byte message. Note the domain-origin comparison itself still has to run inside the wallet against the browser-provided origin; the library can only supply the parsed domain term, not a trustworthy origin.

Suggest implementing the CAIP-122 Stacks profile rather than SIWS-specific code. Same effort, and the serializer stays compatible with SIWX adapters.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Draft SIP is in draft status

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants