FSN#37583 Authenticatie via JWT/ introductie FAC_IDP tabel savepoint

svn path=/Website/trunk/; revision=30482
This commit is contained in:
Jos Groot Lipman
2016-08-31 14:39:06 +00:00
parent 6c20f7627c
commit e3dea60a87
7 changed files with 345 additions and 14 deletions

132
APPL/API2/model_fac_idp.inc Normal file
View File

@@ -0,0 +1,132 @@
<% /*
$Revision$
$Id$
File: model_fac_idp.inc
Description:
Notes: Documentatie in de wiki onder Authenticeren
*/
function fac_idp()
{
this.table = "fac_idp";
this.primary = "fac_idp_key";
this.records_name = "idps";
this.record_name = "idp";
this.autfunction = "WEB_FACFAC";
this.record_title = L("fac_idp");
this.records_title = L("fac_idp_m");
this.fields = {
"id": {
"dbs": "fac_idp_key",
"label": "Key",
"typ": "key",
"seq": "fac_s_fac_idp_key"
},
"code": {
"dbs": "fac_idp_code",
"label": L("fac_idp_code"),
"typ": "varchar"
},
"name": {
"dbs": "fac_idp_omschrijving",
"label": L("fac_idp_omschrijving"),
"typ": "varchar",
"required": true
},
"type": {
"dbs": "fac_idp_type",
"label": L("fac_idp_type"),
"typ": "key",
"required": true,
"LOV": L("fac_idp_typeLOV")
},
"remark": {
"dbs": "fac_idp_opmerking",
"label": L("fac_idp_opmerking"),
"typ": "memo"
},
"secret": {
"dbs": "fac_idp_secret",
"label": L("fac_idp_secret"),
"typ": "varchar"
},
"audience": {
"dbs": "fac_idp_audience",
"label": L("fac_idp_audience"),
"typ": "varchar",
"placeholder": customerId + ".facilitor.nl"
},
"issuer": {
"dbs": "fac_idp_issuer",
"label": L("fac_idp_issuer"),
"typ": "varchar"
},
"algorithm": {
"dbs": "fac_idp_algorithm",
"label": L("fac_idp_algorithm"),
"typ": "varchar"
},
"timeout": {
"dbs": "fac_idp_timeout",
"label": L("fac_idp_timeout"),
"typ": "number"
},
"remote_loginurl": {
"dbs": "fac_idp_remote_loginurl",
"label": L("fac_idp_remote_loginurl"),
"typ": "varchar"
},
"remote_logouturl": {
"dbs": "fac_idp_remote_logouturl",
"label": L("fac_idp_remote_logouturl"),
"typ": "varchar"
},
"usermapping": {
"dbs": "fac_idp_usermapping",
"label": L("fac_idp_usermapping"),
"typ": "varchar"
},
"ipfilter": {
"dbs": "fac_idp_ipfilter",
"label": L("fac_idp_ipfilter"),
"typ": "varchar"
},
"ipauto": {
"dbs": "fac_idp_ipauto",
"label": L("fac_idp_ipauto"),
"typ": "check0"
},
"company": {
"dbs": "prs_bedrijf_key",
"typ": "key",
"foreign": "prs_bedrijf",
"label": L("lcl_idp_company")
},
"department": {
"dbs": "prs_afdeling_key",
"typ": "key",
"foreign": "prs_afdeling",
"label": L("lcl_idp_department")
},
"authorization": {
"dbs": "fac_functie_key",
"label": L("fac_idp_functie_key"),
"typ": "key",
"foreign": fac_functie_foreign()
},
"internal": {
"dbs": "fac_idp_internal",
"label": L("fac_idp_internal"),
"typ": "check0",
"readonly": true
}
}
this.REST_GET = generic_REST_GET(this);
this.REST_POST = generic_REST_POST(this);
this.REST_PUT = generic_REST_PUT(this);
this.REST_DELETE = generic_REST_DELETE(this);
}
%>

View File

@@ -166,7 +166,7 @@ function generateHeaderFunctions (params)
function logOffCallback(json, textStatus)
{
parent.location.href="<%=S("logoff_return_url")%>";
parent.location.href = json.return_url;
};
function logOff()

42
APPL/MGT/fac_idp.asp Normal file
View File

@@ -0,0 +1,42 @@
<%@language = "javascript" %>
<% /*
$Revision$
$Id$
File: fac_idp.asp
Description:
Context:
Notes:
*/
%>
<!-- #include file="../scf/scaffolding.inc" -->
<!-- #include file="../mgt/mgt_tools.inc" -->
<!-- #include file="../api2/model_fac_idp.inc" -->
<%
var this_model = new fac_idp();
scaffolding(this_model,
{
"search": {
"autosearch": true,
"filters": [
"name",
"action"
]
},
"list": {
"columns": [
"id",
"name",
"type",
"remote_loginurl"
]
},
"edit": {
"modal": false
}
});
%>

View File

@@ -251,7 +251,7 @@ if (user_key < 0 && typeof ANONYMOUS_Allowed == "undefined")
}
var url = Session("unauth_url") || S("login_url"); // unauth_url uit shorturl.asp
if (getQParamInt("sso", -1) == 0) // forceer de default
if (getQParam("sso", "") == "0") // forceer de default
url = 'appl/shared/login.asp';
if (!url.match(/^http/))

View File

@@ -15,11 +15,23 @@ var JSON_Result = true;
<%
protectRequest.validateToken();
var result = { success: true };
var result = { success: true,
return_url: S("logoff_return_url")
};
// FACFAC tracken we altijd
if (user.has("WEB_FACFAC"))
shared.trackaction("PRSLOG", user_key, L("lcl_logged_off").format(Session("ASPFIXATION").slice(-6)));
if (Session("idp_key") > 0)
{
var sql = "SELECT fac_idp_remote_logouturl"
+ " FROM fac_idp"
+ " WHERE fac_idp_key = " + Session("idp_key");
var oRs = Oracle.Execute(sql);
if (oRs("fac_idp_remote_logouturl").Value)
result.return_url = oRs("fac_idp_remote_logouturl").Value;
}
if (Session("org_user_key") > 0)
{
var was_key = user_key;

View File

@@ -586,9 +586,9 @@ function SecureSSO(ssoProps)
strReturnURL= strReturnURL.replace("<", "");
strReturnURL= strReturnURL.replace(">", "");
strAction = getFParam("action", "");
if (!strAction && ssoProps.sso > 0) // we zijn begonnen in Facilitor
if (!strAction && ssoProps.ssoURL) // we zijn begonnen in Facilitor en moeten nog naar de klant
{
strReturnURL = ssoProps.sso==1?S("sso_advanced_url"):S("sso_advanced_url_alt");
strReturnURL = ssoURL;
if (!strReturnURL)
{
__DoLog("Secure SSO login error 0");
@@ -867,6 +867,90 @@ function IntegratedSSO()
tryLogin(username,null);
}
}
/* resultaat:
{ err: "Iets niet goed" }
of { header: header,
payload: payload (claim)
signature: signature
}
LET OP: signature is nog niet gevalideerd!
*/
function jwt_decode(token)
{
// check token
if (!token) {
return { err: 'No token supplied' };
}
// check segments
var segments = token.split('.');
if (segments.length !== 3) {
return { err: 'Not enough or too many segments' };
}
// All segment should be base64
var result = { headerSeg: segments[0],
payloadSeg: segments[1],
signature64: segments[2]
}
var oCrypto = new ActiveXObject("SLNKDWF.Crypto");
try
{
// base64 decode and parse JSON
result.header = JSON.parse(oCrypto.base64_decode(result.headerSeg));
result.payload = JSON.parse(oCrypto.base64_decode(result.payloadSeg));
}
catch (s)
{
return { err: "Invalid JSON: " + e.description };
}
var now = new Date().getTime() / 1000;
// Support for nbf and exp claims.
// According to the RFC, they should be in seconds.
if (result.payload.nbf && now < result.payload.nbf) {
return { err: 'Token not yet active' };
}
if (result.payload.exp && now > result.payload.exp) {
return { err: 'Token expired' };
}
return result;
};
function jwt_verify(decoded_jwt, secret, skew)
{
if (decoded_jwt.header.alg != "HS256")
return { err: "Only HS256 is supported" };
var oCrypto = new ActiveXObject("SLNKDWF.Crypto");
var sig = oCrypto.hex_hmac_sha256(secret, decoded_jwt.headerSeg + "." + decoded_jwt.payloadSeg);
var sig64 = oCrypto.hex2base64(sig, false, true); // no padding, urlsafe
var now = new Date().getTime() / 1000;
if (claim.payload.iat)
{
var from = now - skew;
var to = now + skew;
if (claim.payload.iat < from) {
__DoLog("Token expired. Now is {0}, got {1}".format(toDateTimeString(new Date(now * 1000), true), toDateTimeString(new Date(claim.payload.iat * 1000), true)));
return { err: 'Token expired' };
}
if (claim.payload.iat > to) {
__DoLog("Token not yet active. Now is {0}, got {1}".format(toDateTimeString(new Date(now * 1000), true), toDateTimeString(new Date(claim.payload.iat * 1000), true)));
return { err: 'Token not yet active' };
}
}
if (decoded_jwt.signature64 == sig64)
return { success: true }
return { err: "Token signature did not verify" };
}
%>
<script language="VBScript" runat="Server">
'' // Met de beste wil van de wereld kreeg ik dit niet werkend met JScript

View File

@@ -85,13 +85,56 @@ if (user_key < 0)
}
}
if (user_key < 0 && getQParamInt("sso", -1) > 0)
// jwt claim
// TODO: altijd/ ook als user_key > 0?
var jwt = getQParam("jwt", "");
if (user_key < 0 && jwt) // TODO of uit http-header authorization: Bearer of uit form POST
{
var sso = getQParamInt("sso");
var key = sso==1?S("sso_advanced_secret"):S("sso_advanced_secret_alt");
if (!key)
abort_with_warning("SSO Advanced secret is not set");
var ip_restrict = sso==1?S("sso_advanced_autoip"):S("sso_advanced_autoip_alt");
var claim = jwt_decode(jwt);
// __DoLog(claim);
if (claim.err)
abort_with_warning("Invalid JWT: " + claim.err);
var sql = "SELECT *"
+ " FROM fac_idp"
+ " WHERE fac_idp_issuer = " + safe.quoted_sql(claim.payload.iss);
var oRs = Oracle.Execute(sql);
if (oRs.Eof)
abort_with_warning("Unknown JWT issuer: " + claim.payload.iss);
var verify = jwt_verify(claim, oRs("fac_idp_secret").Value, oRs("fac_idp_timeout").Value);
if (verify.err)
abort_with_warning("Invalid JWT: " + verify.err);
// TODO: claim.payload.iss gebruiken om FAC_IDP_CODE te zoeken
// en jwt_verify doen
// en claim.jit registreren/ controleren in fac_session
Session.Contents.Remove("ASPFIXATION"); // Niet moeilijk doen
settings.overrule_setting("login_use_email", 0); // We hebben altijd login gescanned namelijk
if (claim.payload.username)
tryLogin(claim.payload.username, null);
if (user_key < 0 && claim.perslid_key > 0)
doLogin(claim.payload.perslid_key); // je mag ook key meegeven
Session("idp_key") = oRs("fac_idp_key").Value;
var realuser = Session("sso_sgf_realuser");
if (user_key > 0 && realuser)
shared.trackaction("PRSLOG", user_key, L("lcl_logged_on_sso").format(realuser, Request.ServerVariables("REMOTE_ADDR")));
Session.Contents.Remove("sso_sgf_realuser");
}
if (user_key < 0 && getQParam("sso", ""))
{
var sso = getQParam("sso");
var sql = "SELECT *"
+ " FROM fac_idp"
+ " WHERE fac_idp_code = " + safe.quoted_sql(sso);
var oRs = Oracle.Execute(sql);
if (oRs.Eof)
abort_with_warning("Identity provider '{0}' is not configured".format(sso));
var ip_restrict = oRs("fac_idp_ipfilter").Value;
var ip_ok = true;
if (ip_restrict)
{
@@ -100,11 +143,29 @@ if (user_key < 0 && getQParamInt("sso", -1) > 0)
__Log("SSO IP-restrictie {0} versus remote {1}: {2}".format(ip_restrict, ip, ip_ok));
}
if (ip_ok)
SecureSSO({ strSharedKey: key,
Timeout: S("sso_advanced_timeout"),
if (!ip_ok)
abort_with_warning("IP {0} not allowed".format(ip)); // TODO of 400 code forbidden?
if (oRs("fac_idp_type").Value == 3) // die doet het verder zelf
{
SecureSSO({ strSharedKey: oRs("fac_idp_secret").Value,
Timeout: oRs("fac_idp_timeout").Value,
ssoURL: oRs("fac_idp_remote_loginurl").Value,
sso: sso
});
/* keert niet terug */
}
var audience = oRs("fac_idp_audience").Value;
var issuer = oRs("fac_idp_issuer").Value;
var url = oRs("fac_idp_remote_loginurl").Value + "?aud=" + safe.url(audience) + "&iss=" + safe.url(issuer) ;
var return_to = HTTP.urlzelfnoroot() + String(Request.ServerVariables("URL")) + "?" + String(Request.ServerVariables("QUERY_STRING"));
var oCrypto = new ActiveXObject("SLNKDWF.Crypto"); // requires version 4.14
var sig = oCrypto.hex_hmac_sha256(oRs("fac_idp_secret").Value, return_to);
var hmac_to = oCrypto.hex2base64(sig, false, true); // no padding, urlsafe
url += "&return_to={0}&hmac_to={1}".format(safe.url(return_to), hmac_to);
Response.Redirect(url); // die stuurt ons wel terug
oRs.Close();
Response.End;
}
if (user_key < 0)