# Signed Identity

Signed Identity is a more secure way to provide contact identifiers. When the user logs in on your website or mobile app, your backend server will return a signed payload containing the identifiers for this user. This signed payload is called `SignedIdentity`. Take a look at the following sequence diagram:

<figure><img src="/files/Wzjxp0XqGq5suPn4rDqL" alt="" width="375"><figcaption></figcaption></figure>

## Identify Contact with Signed Identity

After the signed identity is retrieved, your web or mobile application can use it to identify the contact as follows:

{% tabs %}
{% tab title="Android (Kotlin)" %}

```kotlin
// Call backend server for user login
val response = userLogin()

// and get signed identity
val signedIdentity = response.signedIdentity

bird.contact.identify( SignedIdentity(signedIdentity) )
```

{% endtab %}

{% tab title="iOS (Swift)" %}

<pre class="language-swift"><code class="lang-swift"><strong>// Coming soon...
</strong></code></pre>

{% endtab %}

{% tab title="Web (Js)" %}

```javascript
// Call your authenticated backend to get a signed identity
// Your backend must verify the user's session before issuing the JWT
const resp = await fetch('/api/bird-identity', {
  method: 'POST',
  credentials: 'include', // sends session cookies for authentication
});
const { signedIdentity } = await resp.json();

await Bird.contact.identify({
  strategy: 'SignedIdentityClaims',
  signedIdentity: signedIdentity,
});
```

{% endtab %}
{% endtabs %}

## Generate Signed Identity

The backend server can generate a signed identity for a user as follows:

* Get the signing key and issuer from the application settings in the Bird dashboard (Developer > Applications > (*your application)* > Overview tab).
* Sign the user identifiers payload using the signing key.

Here is a sample code to get you started:

{% tabs %}
{% tab title="Node.js" %}

```javascript
onst crypto = require('crypto');
const express = require('express');

const app = express();

// These come from your Bird dashboard:
// Developer > Applications > (your app) > Overview > Identity Signing Key
const issuer = process.env.BIRD_ISSUER;       // e.g. "mrn:v1:application:identity-claims-issuer:<workspaceId>/<appId>:1"
const signingKey = process.env.BIRD_SIGNING_KEY; // e.g. "d45013b0eb5355fe..."

function makeSignedIdentity(identifiers) {
    let header = JSON.stringify({
        "alg": "HS256",
        "typ": "JWT",
        "kid": issuer,
    });
    let payload = JSON.stringify({
        identifiers: identifiers,
    });

    let headerBase64 = Buffer.from(header).toString('base64url');
    let payloadBase64 = Buffer.from(payload).toString('base64url');
    let signature = crypto
        .createHmac('sha256', signingKey)
        .update(headerBase64 + "." + payloadBase64)
        .digest('base64url');

    return headerBase64 + "." + payloadBase64 + "." + signature;
}

// Endpoint that your frontend calls to get a signed identity
app.post('/api/bird-identity', async (req, res) => {
    // IMPORTANT: Authenticate the user BEFORE generating a signed identity.
    // Use your existing session/auth system (cookies, JWT, OAuth, etc.)
    // If this endpoint issues JWTs without authentication, any visitor
    // could impersonate any contact.
    const user = await getUserFromSession(req);
    if (!user) {
        return res.status(401).json({ error: 'Not authenticated' });
    }

    // Build identifiers from YOUR trusted user record.
    // Do NOT use identifiers sent from the client request body.
    let identifiers = [
        { key: "emailaddress", value: user.email },
    ];

    let signedIdentity = makeSignedIdentity(identifiers);

    res.json({ signedIdentity });
});

app.listen(3000);
```

{% endtab %}

{% tab title="Go" %}

```go
package main

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/base64"
    "encoding/json"
    "fmt"
    "net/http"
    "os"
    "strings"
)

// These come from your Bird dashboard:
// Developer > Applications > (your app) > Overview > Identity Signing Key
var issuer = os.Getenv("BIRD_ISSUER")         // e.g. "mrn:v1:application:identity-claims-issuer:<workspaceId>/<appId>:1"
var signingKey = os.Getenv("BIRD_SIGNING_KEY") // e.g. "d45013b0eb5355fe..."

type Identifier struct {
    Key   string `json:"key"`
    Value string `json:"value"`
}

func makeSignedIdentity(identifiers []Identifier) (string, error) {
    header, _ := json.Marshal(map[string]string{
        "alg": "HS256",
        "typ": "JWT",
        "kid": issuer,
    })

    payload, _ := json.Marshal(map[string]interface{}{
        "identifiers": identifiers,
    })

    headerBase64 := base64.RawURLEncoding.EncodeToString(header)
    payloadBase64 := base64.RawURLEncoding.EncodeToString(payload)

    dataToSign := headerBase64 + "." + payloadBase64
    h := hmac.New(sha256.New, []byte(signingKey))
    h.Write([]byte(dataToSign))
    signature := base64.RawURLEncoding.EncodeToString(h.Sum(nil))

    return fmt.Sprintf("%s.%s.%s", headerBase64, payloadBase64, signature), nil
}

// Endpoint that your frontend calls to get a signed identity
func birdIdentityHandler(w http.ResponseWriter, r *http.Request) {
    // IMPORTANT: Authenticate the user BEFORE generating a signed identity.
    // Use your existing session/auth system (cookies, JWT, OAuth, etc.)
    // If this endpoint issues JWTs without authentication, any visitor
    // could impersonate any contact.
    user, err := getUserFromSession(r)
    if err != nil || user == nil {
        http.Error(w, `{"error":"Not authenticated"}`, http.StatusUnauthorized)
        return
    }

    // Build identifiers from YOUR trusted user record.
    // Do NOT use identifiers sent from the client request body.
    identifiers := []Identifier{
        {Key: "emailaddress", Value: user.Email},
    }

    signedIdentity, err := makeSignedIdentity(identifiers)
    if err != nil {
        http.Error(w, `{"error":"Failed to generate identity"}`, http.StatusInternalServerError)
        return
    }

    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(map[string]string{
        "signedIdentity": signedIdentity,
    })
}

func main() {
    http.HandleFunc("/api/bird-identity", birdIdentityHandler)
    http.ListenAndServe(":3000", nil)
}
```

{% endtab %}
{% endtabs %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.bird.com/api/client-sdks/contact-profiles/signed-identity.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
