Authentication
Your application and the Weavy environment communicate using a Web API and all requests need to be properly authenticated with Bearer authentication over HTTPS (SSL).
This article explains how to configure seamless authentication between your application and Weavy, and discusses the difference between user-to-server communication with access tokens, and server-to-server communication with API keys. Finally, we present some best practices for how to use and manage tokens.
UIKit authentication
When adding Weavy components to your application, you want the UIKit to act seamlessly on behalf of the logged in (authenticated) user. For this to work you need to configure the UIKit with a token factory. The workflow for authentication is outlined below:
- When a Weavy component is initialized, it requests an
access_token
from thetokenFactory
. - The
tokenFactory
calls a token endpoint on your backend that identifies the logged in user and makes an API request to the Weavy environment which issues anaccess_token
. - With the
access_token
the component can fetch data on behalf of the logged in user and render it in your application.
Token factory
The tokenFactory
is simply an async function
and can be implemented anyway that fits your needs, the only requirement is that it returns a valid access token.
weavy.tokenFactory = async (refresh) => "wyu_**********";
Having said that, the way it's typically implemented is with a fetch request to a token endpoint on your backend as seen in the following example:
weavy.tokenFactory = async (refresh) => {
// fetch access_token from token endpoint on your backend
const response = await fetch("/token?refresh=" + refresh);
if (response.ok) {
const data = await response.json();
// return access_token to UIKit
return data.access_token;
} else {
throw new Error("Could not fetch token from endpoint");
}
};
Token endpoint
The responsibility of the token endpoint is to return an access_token
for the logged in user.
That usually involves the following steps:
- Identify the logged in user (in most web applications users are identified with an authentication cookie).
- Fetch an
access_token
for the user (from local application storage or from the Weavy environment). - Return the
access_token
to thetokenFactory
.
Below you'll find some examples of how you could implement the authentication endpoint in your backend, one for a Node.js server, and one for ASP.NET.
app.get("/token", async (req, res) => {
// get user from session
let username = req.session.user;
// refresh or use existing token?
const refresh = req.query.refresh === "true";
// check local token store for existing access token
if(!refresh && _tokens.find((t) => t.username == username)){
res.json({ access_token: _tokens.find((t) => t.username == username).access_token});
return;
}
// request access token from Weavy environment
let response = await fetch(`${WEAVY_URL}/api/users/${username}/tokens`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${API_KEY}` }
});
if(response.ok){
// store and return access token
let data = await response.json();
_tokens = [..._tokens.filter((t) => t.username !== username), {
username: username,
access_token: data.access_token
}];
res.json({ access_token: data.access_token });
} else{
res.json({ message: "Could not get access token from server" })
}
});
[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 token)) {
// return existing token
return Json(new { access_token = token });
}
// request access token from Weavy environment
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", API_KEY);
var response = await _httpClient.PostAsync($"{WEAVY_URL}/api/users/{uid}/tokens", null);
if (response.IsSuccessStatusCode) {
// store and return access_token
var data = await response.Content.ReadFromJsonAsync<TokenResponse>();
_tokens[uid] = data.AccessToken;
return Json(new { access_token = data.AccessToken });
}
return BadRequest();
}
Example: Response from token endpoint.
{
"access_token": "wyu_**********"
}
To improve response times and reduce unnecessary roundtrips, it is a good practice for your application to store and reuse tokens instead of always requesting new tokens from the Weavy environment.
When the UIKit detects an expired or revoked token, your tokenFactory
will be called with refresh=true
.
If you pass this on to your token endpoint you can use it to clear the invalid token from your application’s storage and request a new token from the Weavy environment.
Token URL
The UIKit comes with a predefined token factory that can be activated by setting the tokenUrl
propery.
The tokenUrl
should point to an endpoint that provides a JSON response with an access_token
.
Whenever the UIKit detects an expired or revoked token, it automatically appends the ?refresh=true
query string to the url.
weavy.tokenUrl = new URL("https://example.com/myapp/token");
Note that you should provide either a
tokenFactory
or atokenUrl
. There's no need to specify both.
Access tokens
When you make API requests on behalf of a user, the request must contain an access_token
in the Authorization
header to associate the request with the user.
This is known as user-to-server communication.
To obtain an access_token
for a user you issue a server-to-server request to the tokens endpoint in the Users API.
API keys
Tokens used in server-to-server communication are called API keys
and can be generated from the management page of your environment.
Note that 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.
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.