This document describes the various aspects of authentication using this authentication server.
Scopes #
Different endpoints require different client scopes. These scopes restrict the clients to only be able to do certain types of actions. Currently these scopes are defined:
profile
: view and edit the users profile,apps
: manage the users applications and devices,apps:<app_id>
: manage theapp_id
app,components
: manage the users components,components:<component_id>
: manage thecomponent_id
component,gateways
: manage the users gateways,gateways:<gateway_id>
: manage thegateway_id
gateway.
Endpoints will check if the authentication method has the required scope
depending on the method of authentication. As a rule of thumb, any endpoint
that has an id (for instance an app id) in it’s URL, requires the specific
scope for that id. For instance GET /applications/foo
will require the
apps:foo
scope, apps
is not enough. On the other hand to perform more
general actions (like listing or creating apps) you will need the more general
scope (eg. apps
).
Rights #
Most actions a user wants to perform on the account server require he has the correct rights for the resource she is trying to access.
Currently there are 3 main categories of rights:
applications
gateways
components
Application rights #
To view and manage an application, a user needs rights to the application. Currently the following rights on applications are defined:
settings
: view and edit the settings of the applicationdelete
: the ability to delete the applicationcollaborators
: the ability to add and remove collaborators or edit their rightsmessages:up:r
: the ability to view messages sent by devices on the applicationmessages:up:w
: the abilty to send messages to the applicationmessages:down:w
: the abilty to send downlink messages to devices on the applicationdevices
: List, view, add, edit and remove the devices for the application on a handler
Gateway rights #
To view and manage a gateway, a user needs rights to the gateway. Currently the following rights on gateways are defined:
gateway:settings
: view and edit the settings of the gatewaygateway:delete
: the ability to delete the gatewaygateway:collaborators
: the ability to add and remove collaborators or edit their rightsgateway:status
: the ability to view the gateway statusgateway:location
: the accurate location of the gateway, if this is not present, a scrambled location will be availablegateway:owner
: view the owener of the gateway
Component rights #
To view and manage a network component, a user needs rights to the component. Currently the following rights on network components are defined:
component:settings
: read and write access to the settings and access key of a network componentcomponent:delete
: the right to delete the network component
Authentication methods #
Currently, there are 3 different methods of authenticating:
- session: sessions on account server, held by encrypted cookies,
- bearer token: OAuth 2.0 bearer json web tokens,
- access key: application access keys.
Session #
This authentication method is only used on the account server website itself to
keep the login status of a user. It automatically grants the profile
scope, and no other scope.
The method is checked for by looking at each requests session
cookie.
The cookies are HTTP-only and secure, to prevent hijacking.
Bearer Token #
This authentication method is the main method of authenticating. It checks
the Authorization
header to be in the following format:
Authorization: Bearer <token>
where <token>
is the jwt access token
containing information about the user.
This token is singed by a secret key that is kept by the account server. You can
find the public decryprtion key using GET /key
endpoint.
The access token
is structured like this:
{
sub: <user id>, // the immutable user ID
client: <client id>, // the ID of the client that requested the token
scope: [ <scope...> ], // array of scopes this token grants
// Apps the user is a collaborator to, by app ID. The value at each key
// is an array of the rights the user has for the specific app.
// Only present when the client requesting the token has the apps scope.
apps: {
<app id>: [
<app right...>
],
},
// Components the user is a collaborator to, by component ID. The value at each key
// is an array of the rights the user has for the specific component.
// Only present when the client requesting the token has the components scope.
components: {
<component id>: [
<component right...>
],
},
// Gateways the user is a collaborator to, by component ID. The value at each key
// is an array of the rights the user has for the specific gateway.
// Only present when the client requesting the token has the gateways scope.
gateways: {
<gateway eui>: [
<gateway right...>
],
},
// these fields are only present when the client requesting the
// token has the profile scope.
username,
email,
created,
name,
valid,
}
Access Key #
You can access certain application endpoints by setting the Authorization
header like:
Authorization: Key <access key>
where <access key>
is an application access key.
Requesting the access token #
To request an access token, initiate an OAuth 2.0 flow at /users/token
.
There’s support for both the password
flow and the authorization_code
flow,
based on the grants of the client you are using.
To prevent tokens from becoming too big (which might happen if a user has a lot of apps, gateways or components), a client can restrict the scope of the access token. By default the scope is not restricted and all entities are encoded in the token.
To restrict the scope, a client should pass the optional scope
parameter and
select the entities it wants to be included in the token:
{
grant_type: 'password',
username: 'username',
password: 'secret',
scope: [
'apps:app_id',
'gateways:gateway_id',
'components:component_id'
]
}
The resulting token will only have rights to edit the application app_id
, the
gateway gateway_id
and the component component_id
, other entities will be
elided.
If the client does not have access to a certain scope (for instance it does not
have the apps
scope), this request would result in a 400 Bad Request
.
Restricted access keys #
While having a lot of advantages, using jwt for access tokens has one problem: the token can become really big. For instance, a user with 20 apps and 30 gateways will have a bigger token than a user with only 4 apps. This is a problem because HTTP restricts the length of headers and tokens that are to big cannot be used any more.
To solve this problem, we restrict the size of each token to have only 10
entities on it (where an entity is an app
, a gateway
or a component
) and
we introduce a new endpoint users/restrict-token
to allow access tokens to be
exchanged for access tokens with similar scope but different specific entities.
So if you have a token with the scope
[ 'apps' ]
, you can exchange it for
another token that has the restricted scope [ 'apps:an_app_id' ]
posting the
following request to /users/restrict-token
, with your access token in the
Authorization: Bearer <token>
header:
{
"scope": [ "apps:another_app_id" ]
}
The result of which would be:
{
"access_token": "<new token for another_app_id>"
}
This way, in effect every token has the same power as a token with all apps
on
it, yet it does not have to as big.
Some tokens should not have this ability though, that is why there is a field on
the token claims: interchangeable
. Only when this is true
, the token will be
interchangeable like this.
Exchanging an Access Key for an Access Token #
When you are building an integration, you will need an application access token,
(which can be validated offline), to start the integration process. However,
users can only provide you with an application access key (which cannot be
validated offline). Therefore, you need to exchange the access key for an
access token. This can be done using the /api/v2/applications/token
endpoint.
It works exactly like a standard OAuth 2.0 password flow. The client initiates a request:
HTTP/1.1 POST /api/v2/applications/token
Content-Type: application/json
Authorization: Basic <basic_auth>
{
"username": "<app_id>",
"password": "<access_key>",
"grant_type": "password"
}
Where <app_id>
is the application id you are requesting the access token for
and <access_key>
is the application access key you are exchanging.
<basic auth>
is constructed following the standard basic auth way:
# pseudo code
basic_auth = base64("<client_id>:<client_secret>")
Where client_id
is the client ID for your integration and <client_secret>
is
the client secret for you integration. For now, these need to be requested
manually with TTN.
If successful, you will receive a response like:
200 Success
Content-Type: application/json
{
"access_token": "<access_token>",
"expires_in": 86400
}
The access_token
is an access token that is scoped to the app you used in the
exchange (eg. it has the [ "app:<app_id>" ]
) scope and has the same rights as
the access key you used to in the exchange.
If the exchange fails for whatever reason, you will get a response like this:
401 Unauthorized
{
"code": 401,
"description": "<error message>"
}
OAuth 2.0 Authorization flow
In this section we describe a sample authorization_code
code flow.
Assuming you have a registered client with these properties:
client_id: foo-client
client_secret: secret
redirect_uri: http://www.example.com/oauth/callback
scope: [ 'apps' ]
1. Make an authorization request #
To be able to use your client, a user will have to authorize it first on the account server. You can tell the user to do so by redirecting to:
https://account.thethingsnetwork.org/users/authorize?client_id=foo-client&redirect_uri=http://www.example.com/oauth/callback&response_type=code
This will ask the user to log in and presents her with a form to authorize your client. If the user authorizes your client, she will be redirected to:
http://www.example.com/oauth/callback?code=<auth code>
You need the <auth code>
from this redirect to go to step 2.
The preference of the user will be stored so this authorization step will redirect immediatly (without showing the form) the next time you do this for the same user.
2. Get the access token #
To get the access token perform the following request:
HTTP/1.1 POST /users/token
Content-Type: application/json
Authorization: Basic <basic_auth>
{
"grant_type": "authorization_code",
"code": "<auth code>",
"redirect_uri: "http://www.example.com/oauth/callback"
}
Where <basic_auth>
is the basic auth digest for your client (client_id
and
client_secret
) and <auth_code>
is the authorization code received from step 1.