Authentication

All communication between your application and Weavy environment goes through the Web API and needs to be properly authenticated.

Authentication is done with Bearer authentication over HTTPS (SSL), and this article explains how to acquire tokens for different scenarios such as server-to-server and user-to-server (including how to configure authentication when adding Weavy building blocks to your application).

Finally, we present some best practices for how to use and manage tokens.

Server authentication

Tokens used in server-to-server communication are called API keys and can be generated from your the environment page on your Weavy account.

An API key does not associate your request with a user account. Instead, permissions are evaluated in the sudo context which gives your app the powers of a "super user". It is therefore very important that the you store the token securely and never expose it in client side code or similar.

While API keys can be configured to never expire, we strongly recommend that you set an expiration date to prevent potential security issues.

User authentication

When your application acts on behalf of a user, it performs a user-to-server request. These requests must contain an access_token for the user in the Authorization header to associate the request with the authenticated user.

Example: Get user information for the authenticated user.

curl https://{WEAVY-SERVER}/api/user
-H "Authorization: Bearer {ACCESS-TOKEN}"   

To obtain an access_token you make a server-to-server request from your server to the tokens endpoint in your Weavy environment (by default access tokens expire after 3600 seconds, but you can specify a custom lifetime with the expires_in parameter when issuing the token).

Example: Issue an access token for user with uid u256.

curl -X POST https://{WEAVY-SERVER}/api/users/u256/tokens 
-H "Authorization: Bearer {API-KEY}"   

Single Sign-On

Our building blocks have built-in functionality for Single Sign-On (SSO) and seamless authentication between your app and Weavy. All you need to do is provide a token endpoint and configure the tokenUrl property. The workflow for authentication then becomes:

Building blockYour backend1. Get access token5. Return access tokenWeavy environment2. Check if exists... 3. Request access token4. Return access token
  1. When the building block needs to perform an API request to the Weavy environment it will ask your backend to supply an access_token for the authenticated user by calling the token endpoint configured by the tokenUrl property.
  2. Your backend will check if it already has an access_token for the user, and if one exists send it back.
  3. If no token exists (or the token has expired) your backend makes a request to the Weavy environment for a new access token.
  4. Weavy returns an access_token for the user (to improve response times and reduce roundtrips, it is good practice to store and reuse it as per step 2).
  5. Your backend returns a JSON response with the access_token to the building block.

Token endpoint

As stated above, you are responsible for supplying an access_token to the building blocks when required. This is usually done by configuring Weavy with a tokenUrl property that points to an endpoint that returns a JSON document with an access_token for the authenticated user.

{
  "access_token": "wyu_qf2llm..."
}

Below you'll find some examples of how to implement the authentication endpoint in your backend, one for a Node.js server, and one for ASP.NET.

Node.js

app.get("/token", async (req, res) => {
    // get user from session
    let username = req.session.user; 
    
    // check local token store for existing access_token
    if(_tokens.find((t) => t.username == username)){        
        res.json({ access_token: _tokens.find((t) => t.username == username).access_token});        
    } 
    
    // if not found, request token from Weavy environment
    let response = await fetch(`${WEAVY-SERVER}/api/users/${username}/tokens`, {
        method: 'POST',
        headers: { 'Authorization': `Bearer ${WEAVY-API-KEY}` }
    });
        
    if(response.ok){
        let data = await response.json();
        // store and return the access token
        _tokens = [..._tokens.filter((t) => t.username !== username), { username: username, access_token: data.access_token}];    
        res.json(data);            
    } else{
        res.json({ message: "Could not get access token from server" })
    }
});

ASP.NET

[HttpGet("~/token")]
public async Task<IActionResult> GetToken(bool refresh = false) {
    // get uid for the authenticated user
    var uid = User.Identity.Name;

    // check local token store for existing access_token
    if (!refresh && _tokens.TryGetValue(uid, out var existingToken)) {
        // return existing token
        return Content(existingToken);
    }

    // get weavy url and api key from config 
    var weavyUrl = _config["WEAVY-SERVER"];
    var apiKey = _config["WEAVY-API-KEY"];

    // request access_token from Weavy
    _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
    var response = await _httpClient.PostAsync($"{weavyUrl}/api/users/{uid}/tokens", null);
    if (response.IsSuccessStatusCode) {
        // read access_token
        var resp = await response.Content.ReadFromJsonAsync<TokenResponse>();
        var accessToken = resp.AccessToken;

        // store and return access_token
        _tokens[uid] = accessToken;
        return Content(accessToken);
    }

    return BadRequest();
}

Best practices

Below are some recommendations to keep in mind when using tokens.

Keep it secret. Keep it safe

A token should be treated like any other credential and revealed only to services that need it.

Give tokens an expiration

Technically, once a token is created, it is valid forever—unless it is revoked and/or configured to expire. This could pose potential issues so you should develop a strategy for expiring and/or revoking tokens.

Embrace HTTPS

Do not send tokens over HTTP connections as those requests can be intercepted and tokens compromised.

Store and reuse

Reduce unnecessary roundtrips that extend your application's attack surface, by storing and re-using tokens. Rather than always creating or requesting new tokens, use the stored tokens during future calls until they expire. How you decide to store your tokens is crucial to defending your application against malicious attacks. Typical solutions include databases and configuration files.