Appearance
Authentication and Authorization
Smart Core BOS checks each request it receives to make sure there is a valid identity associated with the request and that they have the permission to perform the requested action. As a client talking to Smart Core this means you need to attach your id, in the form of an access token, to each request you send to Smart Core.
TIP
Authentication (authn) is about verifying who you are, while authorization (authz) is about checking what you can do. Authentication is your ID card and how you get it, authorization is the bouncer checking your ID to see if you can enter the club.
Access Tokens
Access tokens are the equivalent to a trusted ID card, they contain some information about you like your name, age, and what you're allowed to drive, and something that proves the id is not fake like a watermark or embedded chip. In the case of Smart Core BOS, access tokens are JSON Web Tokens (JWT) instead of a bit of plastic, the information they store about you are called your claims, and the proof comes in the form of a cryptographic signature.
Just like with your physical ID you need to ask someone to re-issue your ID regularly. For UK passports this is every 10 years, for a JWT access token this can be as little as every 5 minutes depending on where you got your token from.
There are many ways to get an access token, each suited to different types of application and use case. Some are useful for applications that interact with users, others for applications that interact with other applications. Smart Core BOS relies on OAuth2 to define how these options work and supports many of the flows defined in the OAuth2 specification either directly or via third party authentication providers.
In general, applications and tools that want to talk to Smart Core BOS will use the Client Credentials flow to get an access token.
Client Credentials
TIP
This is the flow you will likely be using if you are an integrator attempting to get information from a Smart Core building.
If you expect to always be using the same account to talk to Smart Core BOS and no user is involved then this is the flow to use. Your client exchanges a Client ID and Client Secret with the Smart Core BOS node for an access token.
To get an access token you need to send a HTTP POST to the token endpoint of the Smart Core BOS node you want to talk to.
http
POST /oauth2/token HTTP/1.1
Host: smartcore.example.com
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
&client_id={ your-client-id }
&client_secret={ your-client-secret }
Your client id and secret look something like this and should be provided to you by the Smart Core BOS administrator.
Example client id | 3d38f92b-68f2-4483-9e93-c2c0b6852c3c |
Example client secret | 2cigoM8M1lghaMq2zJHf5cIJED_pCGmjywbuSVIP-ek= |
Your client secret should be kept safe and not shared with anyone, this is your password for accessing Smart Core BOS.
After posting to the token endpoint you will receive a JSON response containing your access token.
json
{
"access_token": "{ your-access-token }",
"token_type": "Bearer",
"expires_in": 900
}
This access token will expire in 900
seconds, at which point you should request a new access token using the same token endpoint, client id, and client secret as before.
Other Flows
Smart Core BOS supports many other user centric flows, these are unlikely to be used by integrators and clients to integrate their systems with the building.
TODO
Write up other authentication flows
Using your Access Token
Each request to Smart Core BOS should include a gRPC Authorization
header with a value taken from the access token. You can find documentation on how to add headers to your gRPC requests in the gRPC documentation.
Here's a sample client connection that uses Client Credentials to get a token and add it to all gRPC requests.
go
// configure how to get the token, we're using the Client Credentials Grant
ccConfig := &clientcredentials.Config{
ClientID: clientId,
ClientSecret: clientSecret,
TokenURL: fmt.Sprintf("https://%s/oauth2/token", scHost),
}
// create the client connection with options to use the token for each request
conn, err := grpc.Dial(
fmt.Sprintf("%s:23557", scHost),
// adds the Authorization header to each request,
// refreshing the token whenever it expires
grpc.WithPerRPCCredentials(oauth.TokenSource{
TokenSource: ccConfig.TokenSource(ctx),
}),
// tls settings
grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)),
)
All the code
go
package main
import (
"context"
"crypto/tls"
"fmt"
"net/http"
"os"
"golang.org/x/oauth2"
"golang.org/x/oauth2/clientcredentials"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/oauth"
)
var (
scHost string
clientId string
clientSecret string
)
func init() {
// get information about the environment from somewhere
scHost, _ = os.LookupEnv("SC_HOST") // e.g. smartcore.example.com
clientId, _ = os.LookupEnv("CLIENT_ID")
clientSecret, _ = os.LookupEnv("CLIENT_SECRET")
}
func authInDial(ctx context.Context) (*grpc.ClientConn, error) {
// #region tlsConfig
// setup custom tls config as we need it
tlsConfig := &tls.Config{
InsecureSkipVerify: true, // because we're testing
}
// The ctx is passed to clientcredentials.Config.TokenSource which
// will use this http.Client from the context for requesting the token.
httpClient := &http.Client{
Transport: &http.Transport{
TLSClientConfig: tlsConfig,
},
}
ctx = context.WithValue(ctx, oauth2.HTTPClient, httpClient)
// #endregion tlsConfig
// #region dialAuth
// configure how to get the token, we're using the Client Credentials Grant
ccConfig := &clientcredentials.Config{
ClientID: clientId,
ClientSecret: clientSecret,
TokenURL: fmt.Sprintf("https://%s/oauth2/token", scHost),
}
// create the client connection with options to use the token for each request
conn, err := grpc.Dial(
fmt.Sprintf("%s:23557", scHost),
// adds the Authorization header to each request,
// refreshing the token whenever it expires
grpc.WithPerRPCCredentials(oauth.TokenSource{
TokenSource: ccConfig.TokenSource(ctx),
}),
// tls settings
grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)),
)
// #endregion dialAuth
return conn, err
}
Permissions
Smart Core BOS grants access to actions based on the claims in your access token. You can see these claims by decoding the JWT access token you get from the authentication step. Decoding a JWT is simple, they are made up of three sections separated by a .
character, the first two sections are base64 encoded JSON, the third section is a cryptographic signature which you can ignore for now. The second section contains your claims.
Here's an example of an access token (expired) and the decoded sections
text
eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJhdXRobiIsImV4cCI6MTcwOTI5NzU5MCwiaWF0IjoxNzA5Mjk2NjkwLCJpc3MiOiJhdXRobiIsIm5iZiI6MTcwOTI5NjY5MCwic3ViIjoiM2QzOGY5MmItNjhmMi00NDgzLTllOTMtYzJjMGI2ODUyYzRjIiwiem9uZXMiOlsiZm9vIl19.oIawZHYUzQo9riSYyBn10JOIPOLn1uJ6VE66dsLaFRs
json
{
"alg": "HS256"
}
json
{
"aud": "authn",
"exp": 1709297590,
"iat": 1709296690,
"iss": "authn",
"nbf": 1709296690,
"sub": "3d38f92b-68f2-4483-9e93-c2c0b6852c4c",
"zones": [
"foo"
]
}
Most of the claims in the token are standard JWT claims, aud
and sub
and so on, all useful for validating the token. Smart Core BOS also defines the zones
and roles
claims which it uses to grant access to different resources and devices.
Zones
The zones claim contains the list of all device or area names this token allows you to access. These names are used when you interact with devices via Smart Core, the example we used previously was turning my-device
on or subscribing to changes in my-device
. Each time you make a request where there's a Name
parameter, this name is checked against your zone claims to see if you can access that device.
The names are nested and grant access to all sub-names. If your zones claim includes "floor-1"
then this grants you access to floor-1
but also floor-1/room-1
and floor-1/room-1/light-1
and so on.
Your Smart Core BOS administrator configures your zones when they set you up as a client, if you need access to more zones you should ask them to update your access token. If you are granted access to a zone you can perform any action with those devices.
Roles
Roles describe what type of job you will be doing with Smart Core BOS, from admin
to signage
. Integrators will typically not be assigned roles, instead using zones to grant access to devices. Smart Core applications do use roles when they are user centric, different users log in to these applications and have different roles that grant or deny access to different parts of the application. An operator
might not be create new users whereas an admin
can. A viewer
can't update any devices but can read and subscribe to changes.
Roles are setup by the system administrator and typically not configured in Smart Core BOS itself. Your role is tied to your employment and your job role.