Demo OIDC client to an EGI MasterPortal using the RCauth Delegation ServerThe demonstrator shows example portal integration code to do a full OpenID-connect handshake with a Master Portal plus the /getproxy request to obtain a (optionally VOMS) proxy certificate.The different steps are:
|
<?php
//
// Small demonstration program showing how to obtain a proxy via /getproxy
// endpoint on a MasterPortal. It is provided as is.
//
// Copyright (C) FOM-Nikhef 2016-
// Licensed under the Apache License, Version 2.0 (the "License")
// http://www.apache.org/licenses/LICENSE-2.0
//
// Authors: Mischa Salle (msalle (AT) nikhef.nl)
//
ini_set("display_errors",1);
// Wrapper for curl
function do_curl($url, $fields, &$response, &$error) {
//url-ify the data for the POST
$fields_string="";
foreach($fields as $key=>$value) {
$fields_string .= $key.'='.$value.'&';
}
rtrim($fields_string, '&');
// open connection
$ch = curl_init();
// set the url, number of POST vars, POST data
curl_setopt($ch,CURLOPT_URL, $url);
curl_setopt($ch,CURLOPT_POST, count($fields));
curl_setopt($ch,CURLOPT_POSTFIELDS, $fields_string);
curl_setopt($ch,CURLOPT_HEADER, false);
curl_setopt($ch,CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch,CURLOPT_FOLLOWLOCATION, false);
// force IPv4 resolution
// curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
// next lines are optional, to give cURL debug output in file
$curl_log = fopen("/tmp/curl_demo_stderr.log", "a");
curl_setopt($ch,CURLOPT_STDERR, $curl_log);
curl_setopt($ch,CURLOPT_VERBOSE, true);
// execute post
$response = curl_exec($ch);
$status_code = "";
$error = "";
if (empty($response)) {
// probably connection error
$error = curl_error($ch);
} else {
$status_code = curl_getinfo($ch,CURLINFO_HTTP_CODE);
$info = curl_getinfo($ch);
}
// close connection
curl_close($ch);
return $status_code;
}
// Parses curl status code and prints error when available
function parse_curl_status($type, $status_code, $response, $error) {
if ($status_code >= 300 || !empty($error)) {
print("<html>\n");
print("Error obtaining $type:<BR>\n");
if ($error) {
print("CURL error: ".sanitize($error)."<BR><BR>\n");
} else {
print("Status code: ".$status_code."<BR><BR>\n");
// We might get a error= and error_description= back
if (strpos($response, 'error_description=') !== false) {
$err=parse_ini_string($response);
if (isset($err['error']))
print("<b>".sanitize($err['error'])."</b>.\n<br>\n");
if (isset($err['error_description']))
print("Description: ".sanitize(urldecode($err['error_description']))."\n<br>\n");
} else
print("Response:<BR>\n".sanitize($response)."\n");
}
print("</html>\n");
exit;
}
}
// Prints content of the (json parsed) token response
function print_token_response($values) {
print("<h1>First cURL response (/token request):</h1>\n");
// Decoded response
print("<h2>Parsed response:</h2>\n<pre>\n");
print(sanitize(print_r($values, true)));
print("</pre>\n");
// Decoded ID token
print("<h2>Parsed ID Token:</h2>\n<pre>\n");
foreach (explode(".", $values['id_token']) as $block) {
$subblock=json_decode(base64_decode($block));
print(sanitize(print_r($subblock, true)));
}
print("</pre>\n");
}
// Prints content of the getproxy response
function print_getproxy_response($response) {
$proxy = tempnam("/tmp", "x509up_u");
file_put_contents($proxy, $response);
print("<h1>Second cURL response (/getproxy request):</h1>\n");
print("<h2>Bare response:</h2>\n<pre>\n");
print(sanitize($response));
print("</pre>");
print("<h2>Proxy information:</h2>\n");
print("<pre>\n");
passthru("voms-proxy-info -all -text -file ".$proxy);
unlink($proxy);
print("</pre>\n");
print("</html>");
}
// Sanitize input
function sanitize($input) {
return htmlspecialchars($input, ENT_QUOTES, "UTF-8");
}
////////////////////////////////////////////////////////////////////////
// Start of code
////////////////////////////////////////////////////////////////////////
// Include client variables: $client_id and $client_secret
// This also sets $base_url, the base address of the Master Portal
// MAKE SURE TO KEEP OUT OF WEBROOT!!!
include("/etc/demo/demobasic_elevator.php");
// Set other OIDC parameters
$redirect_uri=urlencode("https://" . $_SERVER["SERVER_NAME"] . $_SERVER["SCRIPT_NAME"]);
// Initialize or resume session: use session ID for state
session_start();
if (empty($_SESSION['count'])) {
$_SESSION['count'] = 1;
} else {
$_SESSION['count']++;
}
// Check we didn't get back an error or we end up in an endless loop
if (isset($_GET['error'])) {
print("<html>\n");
if (isset($_GET['error']))
print("<b>".sanitize($_GET['error'])."</b>.\n<br>\n");
if (isset($_GET['error_description'])) {
print("Description:<br>\n<pre>\n");
print(urldecode(sanitize($_GET['error_description'])));
print("</pre>\n<br>\n");
}
print("</html>\n");
exit;
}
// Where are we: no code: then redirect browser to /authorize
if (!isset($_GET['code'])) {
if (isset($_GET['voms']))
$_SESSION['voms']=true;
else
$_SESSION['voms']=false;
// authorize request
$url = $base_url."/authorize";
$fields = array(
// 'scope' => 'openid edu.uiuc.ncsa.myproxy.getcert',
// 'scope' => 'openid org.cilogon.userinfo edu.uiuc.ncsa.myproxy.getcert',
'scope' => 'openid email profile org.cilogon.userinfo edu.uiuc.ncsa.myproxy.getcert',
'response_type' => 'code',
'client_id' => $client_id,
'redirect_uri' => $redirect_uri,
'state' => hash('sha256', session_id())
);
// Add specific IdP hint: this will bypass the WAYF
if (isset($_GET['idphint']))
$fields['idphint'] = urlencode('https://aai.egi.eu/auth/realms/egi');
//url-ify the data for the POST
$fields_string="";
foreach($fields as $key=>$value) {
$fields_string .= $key.'='.$value.'&';
}
rtrim($fields_string, '&');
// Redirect
header("Location: ".$url."?".$fields_string);
} else {
// first token request
$url = $base_url."/token";
$fields = array(
'grant_type' => 'authorization_code',
'code' => urlencode($_GET['code']),
'redirect_uri' => $redirect_uri,
'client_id' => "$client_id",
'client_secret' => "$client_secret"
);
// Run curl and parse status
$status_code = do_curl($url, $fields, $response, $error);
// parse status code
parse_curl_status("token", $status_code, $response, $error);
// Decoded response
$values=json_decode($response, true);
// Get access token (and ID Token)
$access_token=$values['access_token'];
if (!isset($access_token)) {
print("<html>\nCannot find token in response\n");
print("response=<pre>".sanitize($response)."</pre>\n");
print("url=<pre>".$url."</pre>\n");
print("status_code=<pre>".$status_code."</pre>\n");
print("fields=<pre>");
print(sanitize(print_r($fields, true)));
print("</pre></html>\n");
exit;
}
$id_token=$values['id_token'];
// Print all the output
print_token_response($values);
// Can optionally do a /userinfo callout here
// getproxy request: either with or without VOMS extensions
$url = $base_url."/getproxy";
$fields = array(
'client_id' => "$client_id",
'client_secret' => "$client_secret",
'access_token' => urlencode($access_token),
// 'proxylifetime' => 86399,
);
// Add voms request parameters when needed
if ($_SESSION['voms']) {
$fields['voname'] = 'rcdemo.aarc-project.eu';
$fields['vomses'] = '"rcdemo.aarc-project.eu" "rcvoms.nikhef.nl" "15000" "/DC=org/DC=terena/DC=tcs/C=NL/ST=Noord-Holland/L=Amsterdam/O=Nikhef/CN=pers.nikhef.nl" "rcdemo.aarc-project.eu"';
}
// Run curl and parse status
$status_code = do_curl($url, $fields, $response, $error);
// parse status code
parse_curl_status("proxy", $status_code, $response, $error);
// Print all the output
print_getproxy_response($response);
}
?>