Rate this page

External identities

An alternative to logging in users based on a password kept in the user store is to delegate login to an external identity provider, or external IDP. An external identity provider is a third party authentication server, such as Google, Facebook, an OpenID Connect provider, or a SAML provider.

If a user has an account at an external identity provider supported by the Data Governance Broker, then he or she can log in through this identity provider, and the Data Governance Broker will link the identity provider account (the external identity) to a local account in the user store. The Data Governance Broker’s other identity authenticators can still be used when authenticating with an external identity. For example, a user can log in to the Data Governance Broker via Facebook, and the Data Governance Broker can complete the authentication by prompting the user for a second factor such as a TOTP.

When the Data Governance Broker links an external identity from Google, Facebook, or an OpenID Connect provider, it does so by making an OAuth 2 or OpenID Connect request to the external identity provider. During this process, the external IDP prompts the user to log in and grant permission to share a limited set of data with the Data Governance Broker. The external IDP then grants an access token to the Data Governance Broker. Finally, the Data Governance Broker uses this access token to retrieve profile attributes from the external IDP, which it may use to create or update a local account at the user store.

The Data Governance Broker provides a set of SCIM APIs to complement this authentication feature. These APIs can be used by a privileged application to:

  • Link an existing, logged-in user to an account at an external identity provider.
  • Search for and list a user’s external identities.
  • Read metadata related to a user’s external identities.
  • Unlink a user’s local account from an external identity.

The SCIM API reference describes these APIs in detail. This article will show you how to perform some common operations using Java.

The external identity linking API is a multi-step flow intended for use by privileged management clients. Because this flow requires the user’s active involvement, the client must be an interactive web application.

For this example, let’s assume that we have a queryParams(URI) method that returns a Map of query parameter key/value pairs. Here’s a simple implementation written with Guava.

private static Map<String, String> queryParams(URI uri) {
  return Splitter.on('&').trimResults().withKeyValueSeparator("=")
      .split(uri.getQuery());
}

First, the client submits an ExternalIdentity object to a user’s externalIdentities sub-resource using the HTTP POST method.

final String id = "a13523d8-b6fd-35e0-adca-bdbe434645a5";

String callbackUri = "https://my-client.com/callback";

ExternalIdentity externalIdpLink =
    new ExternalIdentity("Facebook", callbackUri);
externalIdpLink =
    scimService.create("Users/" + id + "/externalIdentities", externalIdpLink);

The response will be another ExternalIdentity object with a different URI in its meta.location field. This URI includes encrypted state information that the Data Governance Broker will use when the client subsequently completes the linking process.

The response also contains a provider redirect URL. This is an OAuth 2 or OpenID Connect request URL that the client will use to redirect the user to the external IDP.

URI providerRedirectUrl = new URI(externalIdpLink.getProviderRedirectUrl());
// Save the externalIdpLink object to the session...
// Now redirect the user's browser to providerRedirectUrl...

After the user logs in to the external IDP and authorizes the request, the external IDP will redirect the user’s browser back to the client’s callback URI. This is a normal OAuth 2 authorization code response, and it will have the ‘state’ value from the provider redirect URL and an authorization code appended as query parameters. If you’ve read the authentication and authorization article in this guide, this should look familiar.

Finally, the client now needs to package up the response from the authorization server and return it to the Data Governance Broker.

URI callbackResponse = new URI("...");

String callbackState = queryParams(callbackResponse).get("state");
String callbackAuthCode = queryParams(callbackResponse).get("code");

// Complete the external IDP linking process.
ObjectNode callbackParameters = JsonUtils.getJsonNodeFactory().objectNode();
callbackParameters.set("code", new TextNode(callbackAuthCode));
callbackParameters.set("state", new TextNode(callbackState));
externalIdpLink.setCallbackParameters(callbackParameters);
externalIdpLink = scimService.replace(externalIdpLink);

The Data Governance Broker will take care of exchanging the authorization code for an access token and updating the user’s local account with attribute values retrieved from the external IDP account.

Retrieving additional user data from the Facebook Graph API

Facebook differs from other external identity provider types because it represents user data as a graph of connected nodes rather than as a flat collection of resources. The Data Governance Broker can only retrieve user data from a single node, the user’s profile. If your organization requires user data from other nodes, then you will need to write a client to do so. To make requests to the Facebook Graph API, this client can use the access token stored with the user’s external identity metadata.

The following example uses the SCIM 2 SDK and the RestFB library to demonstrate how a privileged client can obtain a Facebook access token and request a user’s friends list from the Facebook Graph API.

Filter filter = Filter.hasComplexValue("provider", Filter.eq("type", "facebook"));
ListResponse<ExternalIdentity> response =
    scimService.searchRequest("Users")
        .filter(filter.toString())
        .invoke(ExternalIdentity.class);

if (response.getResources().size() > 0) {
  Optional<ExternalIdentity> facebookIdentity =
      response.getResources().stream().findFirst();
  if (facebookIdentity.isPresent()) {
    String facebookUserId = facebookIdentity.get().getProviderUserId();
    String facebookAccessToken = facebookIdentity.get().getAccessToken();

    FacebookClient facebookClient =
        new DefaultFacebookClient(facebookAccessToken, Version.VERSION_2_7);

    User facebookUser = facebookClient.fetchObject(facebookUserId, User.class);
    Connection<User> facebookFriends =
        facebookClient.fetchConnection(facebookUserId + "/friends", User.class);
    System.out.println(facebookUser.getName() + " has " +
                           facebookFriends.getData().size() + " friends");
  }
}