Skip to content
This repository was archived by the owner on Apr 11, 2023. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"license": "MIT",
"require": {
"simplesamlphp/composer-module-installer": "~1.0",
"firebase/php-jwt": "^4.0"
"firebase/php-jwt": "^4.0",
"ext-json": "*"
}
}
1 change: 0 additions & 1 deletion docs/authirma.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ For example:
'irma' => array(
'authirma:IRMA',
'irma_api_server' => 'https://example.com',
'irma_web_server' => 'https://example.com',
'jwt_privatekeyfile' => 'surfnet-idp-sk.pem',
'jwt_apiserver_publickeyfile' => 'apiserver-pk.pem',
"issuer_id" => "my_issuer_id",
Expand Down
16 changes: 6 additions & 10 deletions lib/Auth/Source/IRMA.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ class sspmod_authirma_Auth_Source_IRMA extends SimpleSAML_Auth_Source {
public $issuer_displayname;
public $requested_attributes;
public $irma_api_server;
public $irma_web_server;

/**
* Constructor for this authentication source.
Expand Down Expand Up @@ -65,9 +64,6 @@ public function __construct($info, &$config) {
if (array_key_exists('irma_api_server', $config)) {
$this->irma_api_server = $config['irma_api_server'];
}
if (array_key_exists('irma_web_server', $config)) {
$this->irma_web_server = $config['irma_web_server'];
}
return;
}

Expand Down Expand Up @@ -125,8 +121,8 @@ public static function handleLogin($authStateId, $irma_result) {
* username/password - if it is, we pass that error up to the login form,
* if not, we let the generic error handler deal with it.
*/
if ($e->getErrorCode() === 'IRMA_INVALIDCREDENTIALS'
|| $e->getErrorCode() === 'IRMA_EXPIREDCREDENTIALS') { // TODO
if ($e->getErrorCode() === 'RESPONSESTATUSNOSUCCESS'
|| $e->getErrorCode() === 'USERABORTED') { // TODO
return $e->getErrorCode();
}
/* Some other error occurred. Rethrow exception and let the generic error
Expand All @@ -145,7 +141,7 @@ public static function handleLogin($authStateId, $irma_result) {
*
* On a successful login, this function should return the users attributes. On failure,
* it should throw an exception. If the error was due to invalid IRMA credentials,
* a SimpleSAML_Error_Error('IRMA_INVALIDCREDENTIALS') should be thrown.
* a SimpleSAML_Error_Error('RESPONSESTATUSNOSUCCESS') should be thrown.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the rationale here for wanting to switch to generic errors instead of the IRMA specific ones?

*
* @param string $irma_result The JWT token from the IRMA API server.
* @return array Associative array with the users attributes.
Expand All @@ -162,15 +158,15 @@ protected function login($irma_credential) {
// validate IRMA credentials
$decoded = (array) JWT::decode($irma_credential,$pubkey,array('RS256'));
if ($decoded["status"] === "EXPIRED")
throw new SimpleSAML_Error_Error('IRMA_EXPIREDCREDENTIALS');
throw new SimpleSAML_Error_Error('USERABORTED');
elseif ($decoded["status"] !== "VALID")
throw new SimpleSAML_Error_Error('IRMA_INVALIDCREDENTIALS');
throw new SimpleSAML_Error_Error('RESPONSESTATUSNOSUCCESS');
$attributes = (array) $decoded['attributes'];
} catch (SimpleSAML_Error_Error $e) {
throw $e;
} catch (Exception $e) {
SimpleSAML\Logger::info('authirma:' . $this->authId . ': Validation error (IRMA credential ' . $irma_credential . ')');
throw new SimpleSAML_Error_Error('IRMA_INVALIDCREDENTIALS', $e);
throw new SimpleSAML_Error_Error('UNHANDLEDEXCEPTION', $e);
}
SimpleSAML\Logger::info('authirma:' . $this->authId . ': IRMA credential ' . $irma_credential . ' validated successfully');
return $attributes;
Expand Down
64 changes: 64 additions & 0 deletions www/get_irma_session.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

/**
* This page renders the IRMA JWT validation request
*
* @author Olav Morken, UNINETT AS.
* @package SimpleSAMLphp
**/

use \Firebase\JWT\JWT;


function get_jwt_key($source) {
$filename = \SimpleSAML\Utils\Config::getCertPath($source->jwt_privatekeyfile);
$pk = openssl_pkey_get_private("file://$filename");
if ($pk === false)
throw new Exception("Failed to load signing key");
return $pk;
}

function get_jwt($source) {
$sprequest = [
"sub" => "verification_request",
"iss" => $source->issuer_id,
"iat" => time(),
"sprequest" => [
"validity" => 60,
"request" => [
"content" => $source->requested_attributes
]
]
];
return JWT::encode($sprequest, get_jwt_key($source), "RS256", $source->issuer_id);
}

if (!array_key_exists('AuthState', $_REQUEST)) {
throw new SimpleSAML_Error_BadRequest('Missing AuthState parameter.');
}
$authStateId = $_REQUEST['AuthState'];

$state = SimpleSAML_Auth_State::loadState($authStateId, sspmod_authirma_Auth_Source_IRMA::STAGEID);
assert('array_key_exists(sspmod_authirma_Auth_Source_IRMA::AUTHID, $state)');
$source = SimpleSAML_Auth_Source::getById($state[sspmod_authirma_Auth_Source_IRMA::AUTHID]);
if ($source === NULL) {
throw new Exception('Could not find authentication source with id ' . $state[sspmod_authirma_Auth_Source_IRMA::AUTHID]);
}

$verification_jwt = get_jwt($source);
$irma_api_server = $source->irma_api_server;
$url = $source->irma_api_server . "/session";
// use key 'http' even if you send the request to https://...
$options = array(
'http' => array(
'header' => "Content-type: text/plain\r\n",
'method' => 'POST',
'content' => $verification_jwt
)
);
$context = stream_context_create($options);
$result = file_get_contents($url, false, $context);

header("Content-Type: application/json");

echo $result;
49 changes: 11 additions & 38 deletions www/irmalogin.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,6 @@
* @package SimpleSAMLphp
**/

use \Firebase\JWT\JWT;

function get_jwt_key($source) {
$filename = \SimpleSAML\Utils\Config::getCertPath($source->jwt_privatekeyfile);
$pk = openssl_pkey_get_private("file://$filename");
if ($pk === false)
throw new Exception("Failed to load signing key");
return $pk;
}

function get_jwt($source) {
$sprequest = [
"sub" => "verification_request",
"iss" => $source->issuer_displayname,
"iat" => time(),
"sprequest" => [
"validity" => 60,
"request" => [
"content" => $source->requested_attributes
]
]
];
return JWT::encode($sprequest, get_jwt_key($source), "RS256", $source->issuer_id);
}

if (!array_key_exists('AuthState', $_REQUEST)) {
throw new SimpleSAML_Error_BadRequest('Missing AuthState parameter.');
}
Expand All @@ -59,8 +34,6 @@ function get_jwt($source) {
$errorCode = NULL;
}

$verification_jwt = get_jwt($source);

$globalConfig = SimpleSAML_Configuration::getInstance();
$t = new SimpleSAML_XHTML_Template($globalConfig, 'authirma:irmalogin.php');
$t->data['stateparams'] = array('AuthState' => $authStateId);
Expand All @@ -71,21 +44,21 @@ function get_jwt($source) {
if (!isset($t->data['head']))
$t->data['head'] = '';
$t->data['head'] .= <<<IRMAHEADERS
<meta name="irma-web-server" value="{$source->irma_web_server}/server/">
<meta name="irma-api-server" value="{$source->irma_api_server}/irma_api_server/api/v2/">
<link href="{$source->irma_web_server}/bower_components/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
<script type="text/javascript" src="{$source->irma_web_server}/bower_components/jquery/dist/jquery.min.js"></script>
<script type="text/javascript" src="{$source->irma_web_server}/bower_components/jwt-decode/build/jwt-decode.js"></script>
<script type="text/javascript" src="{$source->irma_web_server}/client/irma.js"></script>
<meta name="irma-api-server" value="{$source->irma_api_server}">
<script type="text/javascript" src="{$t->data['resources_url']}/jquery-3.4.0.min.js"></script>
<script type="text/javascript" src="{$t->data['resources_url']}/irma.js"></script>
<script type="text/javascript" src="{$t->data['resources_url']}/verify.js"></script>
<script type="text/javascript"> var verification_jwt = "$verification_jwt"; </script>
<script type="text/javascript">
var irma_api_server = "{$source->irma_api_server}";
var authStateId = "$authStateId";
</script>
IRMAHEADERS;

$t->data['errorcodes'] = SimpleSAML\Error\ErrorCodes::getAllErrorCodeMessages();
$t->data['errorcodes']['title']['IRMA_INVALIDCREDENTIALS'] = '{authirma:irma:title_error_invalid}';
$t->data['errorcodes']['title']['IRMA_EXPIREDCREDENTIALS'] = '{authirma:irma:title_error_expired}';
$t->data['errorcodes']['descr']['IRMA_INVALIDCREDENTIALS'] = '{authirma:irma:descr_error_invalid}';
$t->data['errorcodes']['descr']['IRMA_EXPIREDCREDENTIALS'] = '{authirma:irma:descr_error_expired}';
$t->data['errorcodes']['title']['RESPONSESTATUSNOSUCCESS'] = '{authirma:irma:title_error_invalid}';
$t->data['errorcodes']['title']['USERABORTED'] = '{authirma:irma:title_error_expired}';
$t->data['errorcodes']['descr']['RESPONSESTATUSNOSUCCESS'] = '{authirma:irma:descr_error_invalid}';
$t->data['errorcodes']['descr']['USERABORTED'] = '{authirma:irma:descr_error_expired}';

$t->show();
exit();
9 changes: 9 additions & 0 deletions www/resources/irma.js

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions www/resources/jquery-3.4.0.min.js

Large diffs are not rendered by default.

50 changes: 32 additions & 18 deletions www/resources/verify.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,36 @@
$(function() {
var success_fun = function(data) {
console.log("IRMA authentication successful, submitting form");
$("#jwt_result").attr("value", data);
$("form#irma_result_form").submit();
};
var cancel_fun = function(data) {
console.log("Authentication cancelled!");
$("#irma_msg").html('<div class="alert alert-warning" role="alert">IRMA authentication cancelled.</div>');
}
$(function () {
var success_fun = function (data) {
console.log("IRMA authentication successful, submitting form");
$("#jwt_result").attr("value", data);
$("form#irma_result_form").submit();
};
var cancel_fun = function (data) {
console.log("Authentication cancelled!");
$("#irma_msg").html('<div class="alert alert-warning" role="alert">IRMA authentication cancelled.</div>');
};

var error_fun = function(data) {
console.log("Authentication failed!");
console.log("Error data:", data);
$("#irma_msg").html('<div class="alert alert-danger" role="alert">The IRMA authentication has failed.</div>');
}
var error_fun = function (data) {
console.log("Authentication failed!");
console.log("Error data:", data);
$("#irma_msg").html('<div class="alert alert-danger" role="alert">The IRMA authentication has failed.</div>');
};

$("#irma_btn").on("click", function() {
console.log("Button clicked");
IRMA.verify(verification_jwt, success_fun, cancel_fun, error_fun);
$("#irma_btn").on("click", function () {
console.log("Button clicked");
$.getJSON('get_irma_session.php?AuthState=' + authStateId, function (data) {
var irma_session = data.sessionPtr;
var irma_token = data.token;
var promise = irma.handleSession(irma_session);
promise.then(function (data) {
if (data === 'DONE') {
$.get(irma_api_server + '/session/' + irma_token + '/getproof', function (result_jwt) {
success_fun(result_jwt);
});
} else {
cancel_fun(data);
}
});
promise.catch(error_fun);
});
});
});