FSN#37583 Authenticatie via JWT/ introductie FAC_IDP tabel savepoint
svn path=/Website/trunk/; revision=30482
This commit is contained in:
132
APPL/API2/model_fac_idp.inc
Normal file
132
APPL/API2/model_fac_idp.inc
Normal 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);
|
||||
}
|
||||
%>
|
||||
@@ -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
42
APPL/MGT/fac_idp.asp
Normal 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
|
||||
}
|
||||
});
|
||||
%>
|
||||
@@ -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/))
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user