From 8e15bf6365a4515d0ec7f09fff9cc0784b22690d Mon Sep 17 00:00:00 2001 From: Hans Zandbelt Date: Mon, 4 Jul 2016 19:32:25 +0200 Subject: [PATCH 1/8] return decoded JWT contents of the response instead of the JWT itself --- .gitignore | 1 + scripts/getUserDetails.php | 14 +++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d8fe4fa --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/.project diff --git a/scripts/getUserDetails.php b/scripts/getUserDetails.php index 5a9ab43..11466d5 100755 --- a/scripts/getUserDetails.php +++ b/scripts/getUserDetails.php @@ -16,6 +16,15 @@ function pingid_base64url_encode($input) { return str_replace('=', '', strtr(base64_encode($input), '+/', '-_')); } +function pingid_base64url_decode($input) { + $remainder = strlen($input) % 4; + if ($remainder) { + $padlen = 4 - $remainder; + $input .= str_repeat('=', $padlen); + } + return base64_decode(strtr($input, '-_', '+/')); +} + function pingid_jwt_encode($payload, $key, $org_alias, $token) { $header = array( 'alg' => 'HS256', @@ -59,6 +68,7 @@ function pingid_send_request($props, $path, $body) { curl_setopt($ch, CURLOPT_VERBOSE, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $data); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $result = curl_exec($ch); curl_close($ch); @@ -66,11 +76,13 @@ function pingid_send_request($props, $path, $body) { } function pingid_get_user_details($props, $username) { - return pingid_send_request($props, 'getuserdetails/do', array( + $jwt = pingid_send_request($props, 'getuserdetails/do', array( 'getSameDeviceUsers' => true, 'userName' => $username, 'clientData' => null )); + list($headb64, $bodyb64, $cryptob64) = explode('.', $jwt); + return pingid_base64url_decode($bodyb64); } if (count($argv) < 2) { From cdc4bd440a14b40eb6ddfa9abafd7aadd42b2506 Mon Sep 17 00:00:00 2001 From: Hans Zandbelt Date: Thu, 6 Oct 2016 22:00:59 +0200 Subject: [PATCH 2/8] add addUser and deleteUser samples --- scripts/Utils.php | 83 ++++++++++++++++++++++++++++++++++++++ scripts/addUser.php | 45 +++++++++++++++++++++ scripts/deleteUser.php | 35 ++++++++++++++++ scripts/getUserDetails.php | 64 +---------------------------- 4 files changed, 164 insertions(+), 63 deletions(-) create mode 100755 scripts/Utils.php create mode 100755 scripts/addUser.php create mode 100755 scripts/deleteUser.php diff --git a/scripts/Utils.php b/scripts/Utils.php new file mode 100755 index 0000000..80a2b8e --- /dev/null +++ b/scripts/Utils.php @@ -0,0 +1,83 @@ + 'HS256', + 'org_alias' => $org_alias, + 'token' => $token + ); + $segments = array(); + $segments[] = pingid_base64url_encode(json_encode($header)); + $segments[] = pingid_base64url_encode(json_encode($payload)); + $segments[] = pingid_base64url_encode(hash_hmac('SHA256', implode('.', $segments), $key, true)); + return implode('.', $segments); +} + +function pingid_get_timestamp() { + $MDT = 3600 * -6; + return gmdate("Y-m-d H:i:s.000", time() + $MDT); +} + +function pingid_send_request($props, $path, $body) { + $jwt = array( + 'reqHeader' => array( + 'orgAlias' => $props['org_alias'], + 'secretKey' => $props['token'], + 'timestamp' => pingid_get_timestamp(), + 'version' => '4.6', + 'locale' => 'en', + 'sessionId' => null + ), + 'reqBody' => $body + ); + + $data = pingid_jwt_encode( + $jwt, + base64_decode($props['use_base64_key']), + $props['org_alias'], + $props['token']); + + $api_url = $props['admin_url'] . '/rest/4/' . $path; + + #print_r($data); + #exit; + + $headers = array(); + $headers[] = 'Content-Type: application/json'; + $ch = curl_init($api_url); + curl_setopt($ch, CURLOPT_VERBOSE, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $data); + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + $result = curl_exec($ch); + curl_close($ch); + + return $result; +} + +?> diff --git a/scripts/addUser.php b/scripts/addUser.php new file mode 100755 index 0000000..d012320 --- /dev/null +++ b/scripts/addUser.php @@ -0,0 +1,45 @@ + true, + 'email' => $email, + 'fName' => $firstname, + 'lname' => $lastname, + 'userName' => $username, + 'role' => 'REGULAR', + 'clientData' => null + )); + list($headb64, $bodyb64, $cryptob64) = explode('.', $jwt); + return pingid_base64url_decode($bodyb64); +} + +if (count($argv) < 5) { + echo "Usage: $argv[0] \n"; + exit; +} + +$props = parse_ini_file('pingid.properties', false, INI_SCANNER_RAW); + +$response = pingid_add_user($props, $argv[1], $argv[2], $argv[3], $argv[4]); +print $response; + +$json = json_decode($response); +print "\n\n # Activation code is: " . $json->responseBody->activationCode . "\n"; +print " # QR Code URL is: " . $props['admin_url'] . '/QRRedirection?' . base64_encode('act_code=' . $json->responseBody->activationCode) . "\n"; + +?> diff --git a/scripts/deleteUser.php b/scripts/deleteUser.php new file mode 100755 index 0000000..10595fe --- /dev/null +++ b/scripts/deleteUser.php @@ -0,0 +1,35 @@ + $username, + 'clientData' => null + )); + list($headb64, $bodyb64, $cryptob64) = explode('.', $jwt); + return pingid_base64url_decode($bodyb64); +} + +if (count($argv) < 2) { + echo "Usage: $argv[0] \n"; + exit; +} + +$props = parse_ini_file('pingid.properties', false, INI_SCANNER_RAW); + +print pingid_delete_user($props, $argv[1]); + +?> diff --git a/scripts/getUserDetails.php b/scripts/getUserDetails.php index 11466d5..0bf26e6 100755 --- a/scripts/getUserDetails.php +++ b/scripts/getUserDetails.php @@ -12,68 +12,7 @@ this directory. */ -function pingid_base64url_encode($input) { - return str_replace('=', '', strtr(base64_encode($input), '+/', '-_')); -} - -function pingid_base64url_decode($input) { - $remainder = strlen($input) % 4; - if ($remainder) { - $padlen = 4 - $remainder; - $input .= str_repeat('=', $padlen); - } - return base64_decode(strtr($input, '-_', '+/')); -} - -function pingid_jwt_encode($payload, $key, $org_alias, $token) { - $header = array( - 'alg' => 'HS256', - 'org_alias' => $org_alias, - 'token' => $token - ); - $segments = array(); - $segments[] = pingid_base64url_encode(json_encode($header)); - $segments[] = pingid_base64url_encode(json_encode($payload)); - $segments[] = pingid_base64url_encode(hash_hmac('SHA256', implode('.', $segments), $key, true)); - return implode('.', $segments); -} - -function pingid_get_timestamp() { - $MDT = 3600 * -6; - return gmdate("Y-m-d H:i:s.000", time() + $MDT); -} - -function pingid_send_request($props, $path, $body) { - $jwt = array( - 'reqHeader' => array( - 'orgAlias' => $props['org_alias'], - 'secretKey' => $props['token'], - 'timestamp' => pingid_get_timestamp(), - 'version' => '4.6', - 'locale' => 'en', - 'sessionId' => null - ), - 'reqBody' => $body - ); - - $data = pingid_jwt_encode( - $jwt, - base64_decode($props['use_base64_key']), - $props['org_alias'], - $props['token']); - - $headers = array(); - $headers[] = 'Content-Type: application/json'; - $ch = curl_init($props['api-base-url'] . $path); - curl_setopt($ch, CURLOPT_VERBOSE, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $data); - curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - $result = curl_exec($ch); - curl_close($ch); - - return $result; -} +require_once 'Utils.php'; function pingid_get_user_details($props, $username) { $jwt = pingid_send_request($props, 'getuserdetails/do', array( @@ -91,7 +30,6 @@ function pingid_get_user_details($props, $username) { } $props = parse_ini_file('pingid.properties', false, INI_SCANNER_RAW); -$props['api-base-url'] = 'https://idpxnyl3m.pingidentity.com/pingid/rest/4/'; print pingid_get_user_details($props, $argv[1]); From b41dcc58ddf3d5abad40339707b4a8d2389e761a Mon Sep 17 00:00:00 2001 From: Hans Zandbelt Date: Fri, 7 Oct 2016 11:27:00 +0200 Subject: [PATCH 3/8] add offline pairing and refactor --- scripts/Utils.php | 14 +++++++++++-- scripts/addUser.php | 27 +++++++++---------------- scripts/deleteUser.php | 15 +++----------- scripts/finalizeOfflinePairing.php | 27 +++++++++++++++++++++++++ scripts/getActivationCode.php | 31 +++++++++++++++++++++++++++++ scripts/getPairingStatus.php | 26 ++++++++++++++++++++++++ scripts/getUserDetails.php | 17 ++++------------ scripts/startOfflinePairing.php | 32 ++++++++++++++++++++++++++++++ 8 files changed, 144 insertions(+), 45 deletions(-) create mode 100755 scripts/finalizeOfflinePairing.php create mode 100755 scripts/getActivationCode.php create mode 100755 scripts/getPairingStatus.php create mode 100755 scripts/startOfflinePairing.php diff --git a/scripts/Utils.php b/scripts/Utils.php index 80a2b8e..735964f 100755 --- a/scripts/Utils.php +++ b/scripts/Utils.php @@ -11,6 +11,7 @@ on the [Ping Identity developer communities] . See also the DISCLAIMER file in this directory. */ +define('PINGID_API_VERSION', '4.9'); function pingid_base64url_encode($input) { return str_replace('=', '', strtr(base64_encode($input), '+/', '-_')); @@ -49,7 +50,7 @@ function pingid_send_request($props, $path, $body) { 'orgAlias' => $props['org_alias'], 'secretKey' => $props['token'], 'timestamp' => pingid_get_timestamp(), - 'version' => '4.6', + 'version' => PINGID_API_VERSION, 'locale' => 'en', 'sessionId' => null ), @@ -64,8 +65,9 @@ function pingid_send_request($props, $path, $body) { $api_url = $props['admin_url'] . '/rest/4/' . $path; + #echo #print_r($data); - #exit; + #echo $headers = array(); $headers[] = 'Content-Type: application/json'; @@ -80,4 +82,12 @@ function pingid_send_request($props, $path, $body) { return $result; } +function pingid_exec_command($prop_file, $cmd_path, $req_array) { + $props = parse_ini_file($prop_file, false, INI_SCANNER_RAW); + $req_array['clientData'] = null; + $jwt = pingid_send_request($props, $cmd_path . '/do', $req_array); + list($headb64, $bodyb64, $cryptob64) = explode('.', $jwt); + return pingid_base64url_decode($bodyb64); +} + ?> diff --git a/scripts/addUser.php b/scripts/addUser.php index d012320..e0f6ea1 100755 --- a/scripts/addUser.php +++ b/scripts/addUser.php @@ -14,28 +14,19 @@ require_once 'Utils.php'; -function pingid_add_user($props, $username, $firstname, $lastname, $email) { - $jwt = pingid_send_request($props, 'adduser/do', array( - 'activateUser' => true, - 'email' => $email, - 'fName' => $firstname, - 'lname' => $lastname, - 'userName' => $username, - 'role' => 'REGULAR', - 'clientData' => null - )); - list($headb64, $bodyb64, $cryptob64) = explode('.', $jwt); - return pingid_base64url_decode($bodyb64); -} - if (count($argv) < 5) { - echo "Usage: $argv[0] \n"; + echo "Usage: $argv[0] [true|false]\n"; exit; } -$props = parse_ini_file('pingid.properties', false, INI_SCANNER_RAW); - -$response = pingid_add_user($props, $argv[1], $argv[2], $argv[3], $argv[4]); +$response = pingid_exec_command('pingid.properties', 'adduser', array( + 'activateUser' => count($argv) > 5 ? $argv[5] == "true" : "true", + 'email' => $argv[4], + 'fName' => $argv[2], + 'lname' => $argv[3], + 'userName' => $argv[1], + 'role' => 'REGULAR' +)); print $response; $json = json_decode($response); diff --git a/scripts/deleteUser.php b/scripts/deleteUser.php index 10595fe..bd984df 100755 --- a/scripts/deleteUser.php +++ b/scripts/deleteUser.php @@ -14,22 +14,13 @@ require_once 'Utils.php'; -function pingid_delete_user($props, $username) { - $jwt = pingid_send_request($props, 'deleteuser/do', array( - 'userName' => $username, - 'clientData' => null - )); - list($headb64, $bodyb64, $cryptob64) = explode('.', $jwt); - return pingid_base64url_decode($bodyb64); -} - if (count($argv) < 2) { echo "Usage: $argv[0] \n"; exit; } -$props = parse_ini_file('pingid.properties', false, INI_SCANNER_RAW); - -print pingid_delete_user($props, $argv[1]); +print pingid_exec_command('pingid.properties', 'deleteuser', array( + 'userName' => $argv[1] +)); ?> diff --git a/scripts/finalizeOfflinePairing.php b/scripts/finalizeOfflinePairing.php new file mode 100755 index 0000000..61f3cae --- /dev/null +++ b/scripts/finalizeOfflinePairing.php @@ -0,0 +1,27 @@ + \n"; + exit; +} + +print pingid_exec_command('pingid.properties', 'finalizeofflinepairing', array( + 'sessionId' => $argv[1], + 'otp' => $argv[2] +)); + +?> diff --git a/scripts/getActivationCode.php b/scripts/getActivationCode.php new file mode 100755 index 0000000..d726a9a --- /dev/null +++ b/scripts/getActivationCode.php @@ -0,0 +1,31 @@ +\n"; + exit; +} + +$response = pingid_exec_command('pingid.properties', 'getactivationcode', array( + 'userName' => $argv[1] +)); +print $response; + +$json = json_decode($response); +print "\n\n # Activation code is: " . $json->responseBody->activationCode . "\n"; +print " # QR Code URL is: " . $props['admin_url'] . '/QRRedirection?' . base64_encode('act_code=' . $json->responseBody->activationCode) . "\n"; + +?> diff --git a/scripts/getPairingStatus.php b/scripts/getPairingStatus.php new file mode 100755 index 0000000..a7236f1 --- /dev/null +++ b/scripts/getPairingStatus.php @@ -0,0 +1,26 @@ +\n"; + exit; +} + +print pingid_exec_command('pingid.properties', 'pairingstatus', array( + 'activationCode' => $argv[1] +)); + +?> diff --git a/scripts/getUserDetails.php b/scripts/getUserDetails.php index 0bf26e6..0d2b6d3 100755 --- a/scripts/getUserDetails.php +++ b/scripts/getUserDetails.php @@ -14,23 +14,14 @@ require_once 'Utils.php'; -function pingid_get_user_details($props, $username) { - $jwt = pingid_send_request($props, 'getuserdetails/do', array( - 'getSameDeviceUsers' => true, - 'userName' => $username, - 'clientData' => null - )); - list($headb64, $bodyb64, $cryptob64) = explode('.', $jwt); - return pingid_base64url_decode($bodyb64); -} - if (count($argv) < 2) { echo "Usage: $argv[0] \n"; exit; } -$props = parse_ini_file('pingid.properties', false, INI_SCANNER_RAW); - -print pingid_get_user_details($props, $argv[1]); +print pingid_exec_command('pingid.properties', 'getuserdetails', array( + 'getSameDeviceUsers' => true, + 'userName' => $argv[1] +)); ?> diff --git a/scripts/startOfflinePairing.php b/scripts/startOfflinePairing.php new file mode 100755 index 0000000..d36adb1 --- /dev/null +++ b/scripts/startOfflinePairing.php @@ -0,0 +1,32 @@ + [SMS | VOICE | EMAIL ]\n"; + exit; +} + +$response = pingid_exec_command('pingid.properties', 'startofflinepairing', array( + 'username' => $argv[1], + 'type' => $argv[2], + 'pairingData' => $argv[3] +)); +print $response; + +$json = json_decode($response); +print "\n\n # sessionId is: " . $json->responseBody->sessionId . "\n"; + +?> From 7316818e2784ed2cd150b6044f4c59b0137ffd6c Mon Sep 17 00:00:00 2001 From: Hans Zandbelt Date: Fri, 7 Oct 2016 13:18:51 +0200 Subject: [PATCH 4/8] fix generation of the QR code URL --- scripts/addUser.php | 6 +++++- scripts/getActivationCode.php | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/scripts/addUser.php b/scripts/addUser.php index e0f6ea1..6e8c090 100755 --- a/scripts/addUser.php +++ b/scripts/addUser.php @@ -19,7 +19,9 @@ exit; } -$response = pingid_exec_command('pingid.properties', 'adduser', array( +$props_file = 'pingid.properties'; + +$response = pingid_exec_command($props_file, 'adduser', array( 'activateUser' => count($argv) > 5 ? $argv[5] == "true" : "true", 'email' => $argv[4], 'fName' => $argv[2], @@ -29,6 +31,8 @@ )); print $response; +$props = parse_ini_file($props_file, false, INI_SCANNER_RAW); + $json = json_decode($response); print "\n\n # Activation code is: " . $json->responseBody->activationCode . "\n"; print " # QR Code URL is: " . $props['admin_url'] . '/QRRedirection?' . base64_encode('act_code=' . $json->responseBody->activationCode) . "\n"; diff --git a/scripts/getActivationCode.php b/scripts/getActivationCode.php index d726a9a..be4287e 100755 --- a/scripts/getActivationCode.php +++ b/scripts/getActivationCode.php @@ -19,11 +19,15 @@ exit; } -$response = pingid_exec_command('pingid.properties', 'getactivationcode', array( +$props_file = 'pingid.properties'; + +$response = pingid_exec_command($props_file, 'getactivationcode', array( 'userName' => $argv[1] )); print $response; +$props = parse_ini_file($props_file, false, INI_SCANNER_RAW); + $json = json_decode($response); print "\n\n # Activation code is: " . $json->responseBody->activationCode . "\n"; print " # QR Code URL is: " . $props['admin_url'] . '/QRRedirection?' . base64_encode('act_code=' . $json->responseBody->activationCode) . "\n"; From 97f47526ec66c7c850e0bdfb7b62543cadfffcfa Mon Sep 17 00:00:00 2001 From: Hans Zandbelt Date: Mon, 10 Oct 2016 14:20:57 +0200 Subject: [PATCH 5/8] complete README on 3rd-party client integration --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 00367fb..9af906b 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,10 @@ Requires: - PingOne account with PingID service enabled (visit [Ping Identity Developer Site] to get a developer account) ### Installation - -1. Execute the scripts from your command line (ie php getUserDetails.php jsmith) + +1. Enable 3rd-party Client Integration in the PingOne Admin Web GUI (Setup -> PingID -> Client Integration -> Third-party Clients -> Enable). +2. Download the pingid.properties file from PingOne (Setup -> PingID -> Client Integration -> Settings File -> Download) and put in in the scripts directory. +3. Execute the scripts from your command line i.e. `php getUserDetails.php jsmith` ### Disclaimer From b84c2fc14c332de6e319fbe7c95ad409c3deb19f Mon Sep 17 00:00:00 2001 From: Hans Zandbelt Date: Wed, 12 Oct 2016 16:11:54 +0100 Subject: [PATCH 6/8] add workflows --- README.md | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9af906b..3fcbe56 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,30 @@ Requires: ### Installation 1. Enable 3rd-party Client Integration in the PingOne Admin Web GUI (Setup -> PingID -> Client Integration -> Third-party Clients -> Enable). -2. Download the pingid.properties file from PingOne (Setup -> PingID -> Client Integration -> Settings File -> Download) and put in in the scripts directory. +2. Download the `pingid.properties` file from PingOne (Setup -> PingID -> Client Integration -> Settings File -> Download) and put in in the scripts directory. 3. Execute the scripts from your command line i.e. `php getUserDetails.php jsmith` +### Workflows + +1a. Enroll user with mobile device +- `php addUser jdoe John Doe hzandbelt+johndoe@pingidentity.com` + +- `php getUserDetails.php jdoe` + +1b. Add e-mail OTP +- `php startOfflinePairing.php jdoe EMAIL hzandbelt+johndoe@pingidentity.com` +- `php finalizeOfflinePairing.php ` + +2a. Enroll user with e-mail OTP +- `php addUser.php jdoe John Doe hzandbelt+johndoe@pingidentity.com false` +- `php startOfflinePairing.php jdoe EMAIL hzandbelt+johndoe@pingidentity.com` +- `php finalizeOfflinePairing.php ` + +2b. Add mobile device later +- `php getActivationCode.php jdoe` + +- `php getPairingStatus.php ` + ### Disclaimer This software is open sourced by Ping Identity but not supported commercially as such. Any questions/issues should go to the Github issues tracker or discuss on the [Ping Identity developer communities] . See also the DISCLAIMER file in this directory. From db86dab52b790e0fb194919af7d03a1a3e61b9f8 Mon Sep 17 00:00:00 2001 From: Hans Zandbelt Date: Wed, 12 Oct 2016 16:13:02 +0100 Subject: [PATCH 7/8] highlight --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3fcbe56..a25edb7 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Requires: 1a. Enroll user with mobile device - `php addUser jdoe John Doe hzandbelt+johndoe@pingidentity.com` - +*pickup pairing key or QR code URL from output* - `php getUserDetails.php jdoe` 1b. Add e-mail OTP @@ -33,7 +33,7 @@ Requires: 2b. Add mobile device later - `php getActivationCode.php jdoe` - +*pickup pairing key or QR code URL from output* - `php getPairingStatus.php ` ### Disclaimer From 733c82cba1f1d935fdf356f6b3b59b1339b8eb27 Mon Sep 17 00:00:00 2001 From: Hans Zandbelt Date: Fri, 28 Oct 2016 12:34:09 +0200 Subject: [PATCH 8/8] add startAuthentication and authenticateOnline scripts --- scripts/authenticateOnline.php | 28 ++++++++++++++++++++++++++++ scripts/startAuthentication.php | 27 +++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100755 scripts/authenticateOnline.php create mode 100755 scripts/startAuthentication.php diff --git a/scripts/authenticateOnline.php b/scripts/authenticateOnline.php new file mode 100755 index 0000000..94df83e --- /dev/null +++ b/scripts/authenticateOnline.php @@ -0,0 +1,28 @@ +\n"; + exit; +} + +print pingid_exec_command('pingid.properties', 'authonline', array( + 'authType' => 'CONFIRM', + 'spAlias' => 'web', + 'userName' => $argv[1] +)); + +?> diff --git a/scripts/startAuthentication.php b/scripts/startAuthentication.php new file mode 100755 index 0000000..b54f23f --- /dev/null +++ b/scripts/startAuthentication.php @@ -0,0 +1,27 @@ +\n"; + exit; +} + +print pingid_exec_command('pingid.properties', 'startauthentication', array( + 'spAlias' => 'web', + 'userName' => $argv[1] +)); + +?>