Authentication and Authorization with API Key¶
Clients are provided with an API key in JSON format. When this API key is submitted to the authentication server, it returns a JSON Web Token (JWT), which serves as the credential required for authenticating requests to the Bear Cloud API server via gRPC.
API Key Format¶
The credentials are provided in the following JSON format:
{
"api_key": "deca1611-fb00-40b6-be4b-9ed797ae1642",
"scope": "YOUR_SCOPE",
"secret": "2cd66c59-a845-4f16-ac1e-50b50e3878ec"
}
All three fields must be correctly matched for the credentials to be authorized:
api_key: A unique identifier for the credentials within our system.scope: A value fixed at the time the API key is issued. It represents the distributor to which the API key is authorized.secret: A passcode associated with the API key. It is essential that the secret be stored securely.
JWT Generation Examples:¶
curl:¶
curl -X POST https://api-auth.bearrobotics.ai/authorizeApiAccess \
-H "Content-Type: application/json" \
-d '{"api_key":"deca1611-fb00-40b6-be4b-9ed797ae1642",
"scope":"YOUR_SCOPE",
"secret":"2cd66c59-a845-4f16-ac1e-50b50e3878ec"}'
HTTP library (Go):¶
func GetToken(url string, creds string) (string, error) {
resp, err := http.Post(url, "application/json", bytes.NewBufferString(creds))
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return "", fmt.Errorf("failed to get new token: %d", resp.StatusCode)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
return string(body), nil
}
JWT Usage¶
Once the JWT is obtained, it must be included in the Authorization header of each outgoing gRPC request using the following format:
The JWT has an expiration time, specified by the exp field. To maintain uninterrupted access, it is recommended to refresh the JWT periodically, ideally every 15 to 20 minutes.
JWT Usage Example (Go)¶
type TokenCreds struct {
JWT atomic.Value
TransportSecurity bool
}
// GetRequestMetadata gets the current gRPC request metadata,
// with current token. This func gets called before every gRPC query.
func (t *TokenCreds) GetRequestMetadata(_ context.Context, _ ...string)
(map[string]string, error) {
if jwtToken := t.JWT.Load(); jwtToken != nil {
return map[string]string{
"Authorization": "Bearer " + jwtToken.(string),
}, nil
}
return nil, fmt.Errorf("token is not set")
}
func createChannelWithCredentialsRefresh() (*grpc.ClientConn, context.CancelFunc, error) {
var creds = `{"api_key": "deca1611-fb00-40b6-be4b-9ed797ae1642","scope": "YOUR_SCOPE","secret": "2cd66c59-a845-4f16-ac1e-50b50e3878ec"}`
token := TokenCreds{TransportSecurity: true}
ctx, cancelRefresher := context.WithCancel(context.Background())
go func() {
ticker := time.NewTicker(15 * time.Minute)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
slog.Info("Refresher exiting...")
return
case <-ticker.C:
jwt, err := GetToken(
"https://api-auth.bearrobotics.ai/authorizeApiAccess", creds)
if err != nil {
slog.Error("Failed to get new token", "error", err)
}
if err == nil {
token.JWT.Store(jwt)
}
}
}
}()
tls_creds := credentials.NewTLS(&tls.Config{})
conn, err := grpc.NewClient(
serverAddress,
grpc.WithTransportCredentials(tls_creds),
grpc.WithPerRPCCredentials(token))
if err != nil {
log.Fatalf("Failed to create new gRPC client: %v", err)
return nil, nil, err
}
// Make gRPC queries using conn.
// Cancel refresher when no longer needed.
return conn, cancelRefresher, nil
}
Security¶
All connections to the Bear Cloud API server are secured via TLS. The API server's certificates are signed by Google, a trusted Certificate Authority recognized by most systems.