Client Credentials Authentication

Authentication can be granted to a service rather than to a user. Permission must be granted to acquire a service account, which enables Client Credential authentication. To obtain permission, contact [email protected] or your account manager.

Service Account Setup

Once permission has been granted to acquire a service account, send a request to Dev Support [email protected]. with the following information. If you have not yet received an app key, indicate that you need a new key assigned.

Company Name:  
Product Name:  
Owner Name (contact person at company):  
Owner Email:  
App Key (client_id): 

Attach your Public Certificate to the email.

Create Your Public Certificate

A service account requires the encrypting and decrypting of a secret using a private and public key pair. FamilySearch will need a public key certificate, which you can create using OpenSSL.

Step 1: Create Your Private Key

With OpenSSL installed, you can create your private rsa key by issuing the following command.

openssl genrsa -out mykey.pem 2048

The output of this command will be a file named mykey.pem, which contains the private key. You will use this key to encrypt your secret.

Step 2: Create Your Public Certificate

To create your public certificate, issue the following command.

openssl req -new -x509 -key mykey.pem -out mykey-crt.pem -days 730 -subj "/CN=<YourProductName>,OU=<YourOrganizationUnit>,O=<YourOrganization>,L=<YourLocale>,S=<YourState>,C=<YourCountry>"

The result of this command is your public key certificate mykey-cert.pem. Please attach it to the request made to [email protected].

Please Note: The certificate has a 2 year expiration date. Please set yourself a reminder to create a new certificate before that time period expires. The FamilySearch Identity System is capable of handling 2 certificates, so you will be able to submit a new certificate before the time expires.

Authenticating with Client Credentials

To authenticate using client credentials you must create and submit a client secret with your authentication request. The client secret is used to validate that the client is who it says it is. Since the Oauth2 specification does not describe the implementation of client authentication, the FamilySearch Platform implementation of Oauth2 provides a custom mechanism to identify the client by having the client create and sign a time stamp which is submitted to each endpoint as the client_secret parameter. During Oauth2 client registration clients can link to a FamilySearch Platform service account. The following describes how the client must create this request:

  1. The service account has the public key from a public-private key pair provided by the client.
  2. The time stamp is formatted as a decimal string indicating the time in milliseconds after January 1, 1970 00:00:00 GMT.
  3. The time stamp is then signed/encrypted using the private key of the service account associated with the client and Base 64 encoded.

The time stamp is verified/decrypted by FamilySearch Platform and accepted if the time stamp has a time +- 5 minutes of the time on the FamilySearch Platform server at the time the secret is decoded.

Client Credentials Example 1

Java

Sample Java code illustrating invoking the token endpoint with the client credentials grant type. See Client Secret (below) for details on generating the client_secret.

StringBuilder sb = new StringBuilder("https://ident.familysearch.org/cis-web/oauth2/v3/token");
sb.append("?grant_type=client_credentials&client_id=");
sb.append(clientId);
sb.append("&client_secret");
sb.append("=");
sb.append(URLEncoder.encode(secret, "UTF-8"));

URL url = new URL(sb.toString());

HttpsURLConnection con = (HttpsURLConnection)url.openConnection();
con.setRequestMethod("POST");
InputStream ins = con.getInputStream();
InputStreamReader isr = new InputStreamReader(ins);
BufferedReader in = new BufferedReader(isr);
String inputLine;
StringBuilder sb2 = new StringBuilder();

while ((inputLine = in.readLine()) != null)
{
  sb2.append(inputLine);
}

in.close();

JSON json = new JSON();
try{
  HashMap jsonMap = (HashMap)json.fromJSON(sb2.toString());
  if (jsonMap.containsKey("refresh_token")) {
    String refresh_token = (String)jsonMap.get("refresh_token");
  }
}
catch (IllegalStateException e) {
  e.printStackTrace();
}

Client Secret Creation

To obtain a client secret and to verify/decrypt the time stamp, use private key encryption or signing. See the following examples.

Encryption

Private Key Encryption/Public Key Decryption
The following Java code creates a client secret using private key encryption.

KeyStore ks = KeyStore.getInstance("JKS");
char[] password = "mypassword".toCharArray();
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("mykeystore.jks");
ks.load(is, password);
is.close();
PrivateKey privateKey = (PrivateKey)ks.getKey("mykey", password);
// Create the secret with the private key
Security.addProvider(new BouncyCastleProvider());
long timestamp = System.currentTimeMillis();
String sTimestamp = Long.toString(timestamp);
byte[] bytesTimestampUtf8Unencrypted = sTimestamp.getBytes(Charset.forName("UTF-8"));
Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] bytesTimestampUtf8Encrypted = cipher.doFinal(bytesTimestampUtf8Unencrypted);
String encoded = (new BASE64Encoder()).encodeBuffer(bytesTimestampUtf8Encrypted);
String secret = URLEncoder.encode(encoded, "UTF-8");

Signing

Private Key Signing/Public Key Verification
The following Java code creates a client secret using private key signing.

KeyStore ks = KeyStore.getInstance("JKS");
char[] password = "mypassword".toCharArray();
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("mykeystore.jks");
ks.load(is, password);
is.close();
PrivateKey privateKey = (PrivateKey)ks.getKey("mykey", password);
// Create the secret with the private key
Security.addProvider(new BouncyCastleProvider());
long timestamp = System.currentTimeMillis();
String sTimestamp = Long.toString(timestamp);
byte[] bytesTimestampUtf8Unencrypted = sTimestamp.getBytes(Charset.forName("UTF-8"));
Signature signer = Signature.getInstance("SHA512withRSA");
signer.initSign(privateKey);
signer.update(bytesTimestampUtf8Unencrypted);
byte[] bytesTimestampUtf8Encrypted = signer.sign();
String encoded = (new BASE64Encoder()).encodeBuffer(bytesTimestampUtf8Encrypted);
String secret = URLEncoder.encode(encoded, + ":" + timestamp "UTF-8");

Client Credentials Example 2

PHP

Sample PHP code showing how to do the client authentication. This sample code uses Composer for its php library dependency management.

<?php
require "vendor/autoload.php";
function create_secret(){
  $pk = openssl_pkey_get_private("file:///path_to_keys/mykey.pem");
  $timestamp = (string)intval(microtime(true)* 1000);
  openssl_private_encrypt($timestamp,$crypttext,$pk);
  $base64_crypt_time = base64_encode($crypttext);
  return $base64_crypt_time;
}
Guzzle\Http\StaticClient::mount();
$secret = create_secret();
$params = [
  'grant_type' => 'client_credentials',
  'client_id' => 'YOUR-DEV-KEY',
  'client_secret' => $secret
];
$response = Guzzle::post('https://ident.familysearch.org/cis-web/oauth2/v3/token', [
    'query'   => $params,
    'timeout' => 10,
    'debug'   => true
]);
$doc = json_decode($response->getBody());
echo $doc->token;
?>
Here is the Composer package information for this sample code:

{
    "require": {
        "guzzle/guzzle": "~3.7"
    }
}

Client Credentials Example 3

Ruby

Sample Ruby code showing how to do the client authentication. This sample code uses Bundler to manage the required gems to run this code. See the Gemfile below the sample.

require 'openssl'
require 'base64'
require 'faraday'
require 'faraday_middleware'
def create_secret()
  private_key = OpenSSL::PKey::RSA.new File.read 'keys/mykey.pem'
  time_in_milliseconds = (Time.new.to_f * 1000).to_i
  encrypted_time = private_key.private_encrypt time_in_milliseconds.to_s
  base64_crypt_time = Base64.encode64 encrypted_time
end
# Using Faraday, but this could easily be done with any other HTTP library.
conn = Faraday.new() do |faraday|
  faraday.response :logger                  # log requests to STDOUT
  faraday.adapter  Faraday.default_adapter  # make requests with Net::HTTP
  faraday.response :json, :content_type => /\bjson$/
end
secret = create_secret
params = {
  'grant_type' => 'client_credentials',
  'client_id' => 'YOUR-KEY',
  'client_secret' => secret
}
response = conn.post do |req|
  req.url "https://ident.familysearch.org/cis-web/oauth2/v3/token", params
end
puts response.body['token']

Here is the Gemfile for bundler.

source "https://rubygems.org"
gem "faraday", "~> 0.8.9"
gem "faraday_middleware", "~> 0.9.0"

Sample Request and Response

Request

POST /cis-web/oauth2/v3/token
Accept: application/json
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&client_id=Q9DB-R9RS-78MC-52M4-N3J9-W5VX-KT25-PK83&client_secret=KCnu5RD
SZHpiwa3G9xssuIE2vVO3XqBUNZwIDUk3e244x%2F6TGo5M9HD06dlgqxv9YP6bhk%2F7YjX84JE2JdZbe2n6G5s3TDcHr35OerqHu
Zq5Fs7BI63KnC7ejv7E5Of5UJOojXqUxQSKIQ07CH7wfwniyHtb1xkk%2BVVPQfVFKj8X42r%2BFwkNXuYUOzTdzoFgktwuWAVMWZ
fLfLPMrz7z%2BtqgUiJ9h0EWdfpNsrrVpip3ivxc3KLDUWyhgGu4eouxYCrr2V8%2FTMhnch7eyBtBEgHAT2graMGjXxiH%2BjhB
SLlwXECm673p6u8%2BAJ40QoofXPyhVkMt6cD5JowHp38Yjw%3D%3D

Response

HTTP/1.1 200 OK
Transfer-encoding: chunked
Cache-control: no-cache, no-store, no-transform, must-revalidate, max-age=0
Vary: Accept-Encoding
Vary: Accept, Accept-Language, Accept-Encoding, Expect
Date: Thu, 14 Jan 2016 22:39:55 GMT
Content-type: application/json
X-processing-time: 37
{
  "access_token" : "2YoTnFdFEjr1zCsicMWpAA",
  "token_type" : "family_search"
}