Skip to main content

Stellar Authentication

info

This guide is available on three different programming languages: Typescript, Kotlin and Flutter (Dart). You can change the shown version on each page via the buttons above.

Wallets connect to anchors using a standard way of authentication via the Stellar network defined by the SEP-10 standard.

This guide will cover all ways to use SEP-10 to authenticate with an anchor.

Creating Authentication Key​

Custodial wallets only

First, let's create an authentication key. While you can use the same key for authentication and sending funds, it's recommended to split the responsibilities. In your application, you will have one or more fund keypairs (keypairs for the accounts that hold funds and initiate and receive transactions) and one authentication key.

The authentication key is only used for authentication purposes and doesn't need to hold any funds. Note that you don't need to create an account for this keypair either.

Go to the Stellar Lab and generate a keypair. The secret key must be handled securely, because it will be used for authentication.

Basic Authentication​

Let's do a basic authentication. In this example, we will use wallet SDK to create an authentication token.

First, let's create an anchor object to work with the anchor you are integrating with. In this example, we will be using a reference anchor implementation with the home domain testanchor.stellar.org

const anchor = wallet.anchor({ homeDomain: "https://testanchor.stellar.org" });

Next, authenticate with the authKey created earlier:

const authKey = SigningKeypair.fromSecret("my secret key");
const sep10 = await anchor.sep10();

const authToken = await sep10.authenticate({ accountKp: authKey });

For non-custodial wallets, you want to use the user's private key as an authKey.

Home Domain (Optional)​

The home domain is the optional parameter for SEP-10 authentication, when a single auth server is shared between multiple domains. Some anchors may require you to provide this argument. The SDK automatically sets the home_domain parameter in all SEP-10 requests.

Client Domain (Optional)​

Non-custodial wallets only
caution

Some anchors may require the client_domain to always be present as part of the request, even for non-custodial wallets.

Client domain is used by anchors to verify the origin of user's request (which wallet this user is using?). This is particularly useful for anchors for integrating with non-custodial wallets.

Supporting client_domain comes in two parts, the wallet's client and the wallet's server implementations. In this setup, we will have an extra authentication key. This key will be stored remotely on the server. Using the SEP-1 info file, the anchor will be able to query this key and verify the signature. As such, the anchor would be able to confirm that the request is coming from your wallet, belonging to wallet's client_domain.

Client Side​

First, let's implement the client side. In this example we will connect to a remote signer that signs transactions on the endpoint https://demo-wallet-server.stellar.org/sign for the client domain demo-wallet-server.stellar.org.

const signer = new DomainSigner(
"https://demo-wallet-server.stellar.org/sign",
{},
);

const getAuthToken = async () => {
return anchor.sep10().authenticate({
accountKp,
walletSigner: signer,
clientDomain: "demo-wallet-server.stellar.org",
});
};
danger

The demo-wallet signing endpoint is not protected for anybody to use. Your production URL must be protected, otherwise anybody could impersonate your wallet's user.

Let's add authentication with a bearer token. Simply update the request transformer:

const signer = new DomainSigner("https://demo-wallet-server.stellar.org/sign", {
Authorization: `Bearer ${authToken}`,
});

Finally, with the approach above we define the signer and client domain per request. If you want to define it once and use it for every authentication call your application is making, you can do so via changing the configuration:

const appCfg = new ApplicationConfiguration(
new DomainSigner("https://my-domain.com/sign", { ...headers }),
undefined,
"my-domain.com",
);

This is particularly useful for integrating with multiple anchors.

Server Side​

Next, let's implement the server side.

First, generate a new authentication key that will be used as a client_domain authentication key.

Next, create a SEP-1 toml file placed under <your domain>/.well-known/stellar.toml with the following content:

ACCOUNTS = [ "Authentication public key (address)" ]
VERSION = "0.1.0"
SIGNING_KEY = "Authentication public key (address)"
NETWORK_PASSPHRASE = "Test SDF Network ; September 2015"

Don't forget to change the network passphrase for Mainnet deployment.

Finally, let's add server implementation. This sample implementation uses express framework:

app.post("/sign", (req, res) => {
const envelope_xdr = req.body.transaction;
const network_passphrase = req.body.network_passphrase;
const transaction = new Transaction(envelope_xdr, network_passphrase);

if (Number.parseInt(transaction.sequence, 10) !== 0) {
res.status(400);
res.send("transaction sequence value must be '0'");
return;
}

transaction.sign(Keypair.fromSecret(SERVER_SIGNING_KEY));

res.set("Access-Control-Allow-Origin", "*");
res.status(200);
res.send({
transaction: transaction.toEnvelope().toXDR("base64"),
network_passphrase: network_passphrase,
});
});

You can see examples of both the wallet and server implementations here. As mentioned before, this server sample implementation doesn't have any protection against unauthorized requests, so you must add authorization checks as part of the request.