Authentication can be granted to an app rather than to a user. To obtain client credential authentication, you must first contact devsupport@familysearch.org to obtain special permission and assignment.
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:
- The service account has the public key from a public-private key pair provided by the client.
- The time stamp is formatted as a decimal string indicating the time in milliseconds after January 1, 1970 00:00:00 GMT.
- 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.
public String getAccessToken(String clientId, String clientSecret) throws Exception { HttpClient client = new HttpClient(); post.addParameter("grant_type", "client_credentials"); try { |
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" } |