Get authorization tokens

What is a token?

Fleet Engine requires the use of JSON Web Tokens (JWTs) signed by an appropriate service account for API method calls from low-trust environments. Low-trust environments include smartphones and browsers. A JWT originates on your server, which is a fully-trusted environment. The JWT is signed, encrypted, and passed to the client for subsequent server interactions until it expires or is no longer valid.

Your backend should authenticate and authorize against Fleet Engine using standard Application Default Credentials mechanisms. Make sure to use JWTs that have been signed by an appropriate service account. For a list of service-account roles, see Fleet Engine service account roles in Fleet Engine Basics.

For more information about JSON Web Tokens, see JSON Web Tokens in Fleet Engine Essentials.

How clients get tokens?

Once a driver or consumer logs in to your app using the appropriate authentication credentials, any updates issued from that device must use appropriate authorization tokens, which communicates to Fleet Engine the permissions for the app.

As the developer, your client implementation should provide the ability to do the following:

  • Fetch a JSON Web Token from your server.
  • Reuse the token until it expires to minimize token refreshes.
  • Refresh the token when it expires.

The GMTDAuthorization protocol fetches JSON Web tokens at location update time based on the GMTD AuthorizationContext object. The SDK must package the tokens with the update information to send to Fleet Engine. Make sure that your server-side implementation can issue tokens before initializing the SDK.

For details of the tokens expected by Fleet Engine, see Issue JSON Web Tokens for Fleet Engine.

The providerID is the same as the Project ID of your Google Cloud Project. For information on setting up the Google Cloud Project, see Create your Fleet Engine project.

Example of an authentication token fetcher

The following example shows an implementation of the GMTDAuthorization protocol.

Swift

import GoogleRidesharingDriver

private let providerURL = "INSERT_YOUR_TOKEN_PROVIDER_URL"

class SampleAccessTokenProvider: NSObject, GMTDAuthorization {
  private struct AuthToken {
    // The cached vehicle token.
    let token: String
    // Keep track of when the token expires for caching.
    let expiration: TimeInterval
    // Keep track of the vehicle ID the cached token is for.
    let vehicleID: String
  }

  enum AccessTokenError: Error {
    case missingAuthorizationContext
    case missingData
  }

  private var authToken: AuthToken?

  func fetchToken(
    with authorizationContext: GMTDAuthorizationContext?,
    completion: @escaping GMTDAuthTokenFetchCompletionHandler
  ) {
    // Get the vehicle ID from the authorizationContext. This is set by the Driver SDK.
    guard let authorizationContext = authorizationContext else {
      completion(nil, AccessTokenError.missingAuthorizationContext)
      return
    }
    let vehicleID = authorizationContext.vehicleID

    // If appropriate, use the cached token.
    if let authToken = authToken,
      authToken.expiration > Date.now.timeIntervalSince1970 && authToken.vehicleID == vehicleID
    {
      completion(authToken.token, nil)
      return
    }

    // Otherwise, try to fetch a new token from your server.
    let request = URLRequest(url: URL(string: providerURL))
    let task = URLSession.shared.dataTask(with: request) { [weak self] data, _, error in
      guard let strongSelf = self else { return }
      guard error == nil else {
        completion(nil, error)
        return
      }

      // Replace the following key values with the appropriate keys based on your
      // server's expected response.
      let vehicleTokenKey = "VEHICLE_TOKEN_KEY"
      let tokenExpirationKey = "TOKEN_EXPIRATION"
      guard let data = data,
        let fetchData = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
        let token = fetchData[vehicleTokenKey] as? String,
        let expiration = fetchData[tokenExpirationKey] as? Double
      else {
        completion(nil, AccessTokenError.missingData)
        return
      }

      strongSelf.authToken = AuthToken(
        token: token, expiration: expiration, vehicleID: vehicleID)
      completion(token, nil)
    }
    task.resume()
  }
}

Objective-C

#import "SampleAccessTokenProvider.h"
#import <GoogleRidesharingDriver/GoogleRidesharingDriver.h>

// SampleAccessTokenProvider.h
@interface SampleAccessTokenProvider : NSObject<GMTDAuthorization>
@end

static NSString *const PROVIDER_URL = @"INSERT_YOUR_TOKEN_PROVIDER_URL";

// SampleAccessTokenProvider.m
@implementation SampleAccessTokenProvider{
  // The cached vehicle token.
  NSString *_cachedVehicleToken;
  // Keep track of the vehicle ID the cached token is for.
  NSString *_lastKnownVehicleID;
  // Keep track of when tokens expire for caching.
  NSTimeInterval _tokenExpiration;
}

-   (void)fetchTokenWithContext:(nullable GMTDAuthorizationContext *)authorizationContext
                   completion:(nonnull GMTDAuthTokenFetchCompletionHandler)completion {

  if (!completion) {
   NSAssert(NO, @"%s encountered an unexpected nil completion.", __PRETTY_FUNCTION__);
   return;
  }

  // Get the vehicle ID from the authorizationContext. This is set by the Driver SDK.
  NSString *vehicleID = authorizationContext.vehicleID;
  if (!vehicleID) {
    NSAssert(NO, @"Vehicle ID is missing from authorizationContext.");
    return;
  }

  // Clear cached vehicle token if vehicle ID has changed.
  if (![_lastKnownVehicleID isEqual:vehicleID]) {
    _tokenExpiration = 0.0;
    _cachedVehicleToken = nil;
  }
  _lastKnownVehicleID = vehicleID;

  // Clear cached vehicletoken if it has expired.
  if ([[NSDate date] timeIntervalSince1970] > _tokenExpiration) {
    _cachedVehicleToken = nil;
  }

  // If appropriate, use the cached token.
  if (_cachedVehicleToken) {
    completion(_cachedVehicleToken, nil);
    return;
  }
  // Otherwise, try to fetch a new token from your server.
  NSURL *requestURL = [NSURL URLWithString:PROVIDER_URL];
  NSMutableURLRequest *request =
      [[NSMutableURLRequest alloc] initWithURL:requestURL];
  request.HTTPMethod = @"GET";
  // Replace the following key values with the appropriate keys based on your
  // server's expected response.
  NSString *vehicleTokenKey = @"VEHICLE_TOKEN_KEY";
  NSString *tokenExpirationKey = @"TOKEN_EXPIRATION";
  __weak typeof(self) weakSelf = self;
  void (^handler)(NSData *_Nullable data, NSURLResponse *_Nullable response,
                  NSError *_Nullable error) =
      ^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) {
        typeof(self) strongSelf = weakSelf;
        if (error) {
          completion(nil, error);
          return;
        }

        NSError *JSONError;
        NSMutableDictionary *JSONResponse =
            [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&JSONError];

        if (JSONError) {
          completion(nil, JSONError);
          return;
        } else {
          // Sample code only. No validation logic.
          id expirationData = JSONResponse[tokenExpirationKey];
          if ([expirationData isKindOfClass:[NSNumber class]]) {
            NSTimeInterval expirationTime = ((NSNumber *)expirationData).doubleValue;
            strongSelf->_tokenExpiration = [[NSDate date] timeIntervalSince1970] + expirationTime;
          }
          strongSelf->_cachedVehicleToken = JSONResponse[vehicleTokenKey];
          completion(JSONResponse[vehicleTokenKey], nil);
        }
      };
  NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
  NSURLSession *mainQueueURLSession =
      [NSURLSession sessionWithConfiguration:config delegate:nil
                               delegateQueue:[NSOperationQueue mainQueue]];
  NSURLSessionDataTask *task = [mainQueueURLSession dataTaskWithRequest:request completionHandler:handler];
  [task resume];
}

@end

What's next

Initialize the Driver SDK