diff --git a/1.cfm b/1.cfm
index 0e63b05..1892075 100644
--- a/1.cfm
+++ b/1.cfm
@@ -1,9 +1,15 @@
-t test
+test
-
+
+
-
+
+
+
+ Log Out: #request.logout_url#
+
+
diff --git a/Application.cfc b/Application.cfc
index ed455b8..5546152 100644
--- a/Application.cfc
+++ b/Application.cfc
@@ -19,6 +19,7 @@
+
@@ -74,7 +75,8 @@
-
+
+
@@ -138,6 +140,7 @@
+
@@ -150,12 +153,16 @@
-
+
+
+
+
+
-
+
-
- //проверить sso
- //? сделать рефреш
- //? сделать логаут
- //? сделать отображение ошибки IDP
- //+ сделать получение сертификата
-
+ lock scope="session" type="readonly" timeout="3" {
if (structKeyExists(session, "auth")) {
- request.auth = structCopy(session.auth);
- } else {
- //
- session.auth_state=createGUID().toString()
-
-
- jwt = new lib.jwt(this.client_secret);
- idp = new lib.oauth2(this.client_id, this.client_secret, this.auth_endpoint, this.access_token_endpoint, this.redirect_uri);
-
- /*
- часто появляется ошибка: после неудачного запроса к IDP
- в адресной строке браузера остается все, с чем его редиректил IDP,
- а повторный запрос с этим контентом уже не работает. Получаем 400 Bad Request -
- может быть, KeyCloak так защищается от циклических редиректов
- Не очень понятно, как предотвратить повторный запрос (можно, конечно, использовать сессию, но странно)
- */
- //writeOutput(getTickCount()-request.startTickCount);
- if(structKeyExists(url, "code")) { //обрабатываем редирект от IDP *** наличие поля code в URL введет нас в заблуждение,
- //но эvar = data, label =то только без валидной сессии
- //dump(var = url.code, label = "URL.code");
- // Request access token from idp with the
- // authorization code that we got via the URL
- //aFormFields = [];
- //data = idp.makeAccessTokenRequest((url.code).content, aFormFields);
- try {
- writeOutput("idp.makeAccessTokenRequest");
- writeOutput(getTickCount()-request.startTickCount);
- resp = idp.makeAccessTokenRequest(url.code/*, aFormFields*/);
- writeOutput("idp.makeAccessTokenRequest Done");
- writeOutput(getTickCount()-request.startTickCount);
- //dump(resp);
- data = deserializeJson(resp.content);
- //writedump(data);
- // Print full response from idp
- //dump(var = data, label = "makeAccessTokenRequest - Response from idp");
-
- //echo('Refresh token #data.refresh_token#');
-
- session.auth.expires_in = data.expires_in;
- session.auth.refresh_expires_in = data.refresh_expires_in;
- session.auth.access_token = data.access_token;
- session.auth.refresh_token = data.refresh_token;
- session.auth.id_token = data.id_token;
- session.auth.token_type = data.token_type;
-
- session.idp_response_content = structCopy(data);
-
- //flush()
- lock scope="application" type="readonly" timeout="1" {
- token_data = (jwt.decode(data.access_token, application.idpCertificate,"RS256"));
- }
- writeOutput("jwt.decode Done");
- writeOutput(getTickCount()-request.startTickCount);
- writedump(token_data);
- session.auth.login = token_data.preferred_username;
- session.auth.wz = token_data.ClientID;
- session.auth.fullname = token_data.name;
- session.auth.groups = token_data.groups;
- session.auth.session_state = token_data.session_state;
- session.auth.sid = token_data.sid;
- session.auth.auth_time = token_data.auth_time;
- session.auth.exp = token_data.exp;
- session.auth.iat = token_data.iat;
- //session.sid = token_data.sid;
- session.token_data = structCopy(token_data);
-
- location(this.redirect_uri,false);
- } catch (e) {echo('
****************** #e.message# : #e.detail# **********');}
-
- } else {
- strURL = idp.buildRedirectToAuthURL({"scope":'openid profile email',"state":session.auth_state,"allow_signup":false});
- echo('self-made Auth link (idp) #strURL#
');
- location(strUrl,false);
- }
-
+ request.auth = structCopy(session.auth);
}
+ }
+ if (structKeyExists(request, "auth") AND Now() < request.auth.exp) {
- } // lock session exclusive
+ if (dateDiff("s", request.auth.iat, Now()) > request.auth.refresh_expires_in/1.5 AND CGI.REQUEST_METHOD EQ "GET") {
+ // доля времени, после которой происходит рефреш, прибита гвоздями
+ // По всей видимости, рефреш будет ломать обращения POSТ
+ // В связи с чем предлагается дождаться GET
+ // Приклеивать query_string как-то не пришлось, браузер сам справляется
+
+ // refresh
+ idp = new lib.oauth2(this.client_id, this.client_secret, this.auth_endpoint, this.access_token_endpoint, this.redirect_uri);
+ resp = idp.refreshAccessTokenRequest(request.auth.refresh_token);
+
+ lock scope="application" type="readonly" timeout="1" {
+ idpCertificate = application.idpCertificate;
+ }
+
+ auth = parseIdpresponse(resp, idpCertificate, this.client_secret); //все эти сложности из-за опасений насчет многопоточности и блокировок
+
+ lock scope="session" type="exclusive" timeout="1" {
+ session.auth = auth;
+ }
+ request.auth = auth;
+ writeDump(auth);
+ }
+ } else {
+ // сессии SSO нет
+ // или токен протух
+ request.auth_state = createGUID().toString();
+ lock scope="session" type="exclusive" timeout="1" {
+ structDelete(session, "auth"); // зачистили сессию от данных SSO
+ session.auth_state = request.auth_state;
+ }
+
+ idp = new lib.oauth2(this.client_id, this.client_secret, this.auth_endpoint, this.access_token_endpoint, this.redirect_uri);
+ /*
+ часто появляется ошибка: после неудачного запроса к IDP
+ в адресной строке браузера остается все, с чем его редиректил IDP,
+ а повторный запрос с этим контентом уже не работает. Получаем 400 Bad Request -
+ может быть, KeyCloak так защищается от циклических редиректов
+ Не очень понятно, как предотвратить повторный запрос (можно, конечно, использовать сессию, но странно)
+ */
+ //writeOutput(getTickCount()-request.startTickCount);
+ if(structKeyExists(url, "code")) { //обрабатываем редирект от IDP *** наличие поля code в URL введет нас в заблуждение,
+
+ try {
+ //writeOutput("idp.makeAccessTokenRequest");
+ //writeOutput(getTickCount()-request.startTickCount);
+ resp = idp.makeAccessTokenRequest(url.code);
+
+ lock scope="application" type="readonly" timeout="1" {
+ idpCertificate = application.idpCertificate;
+ }
+
+ auth = parseIdpresponse(resp, idpCertificate, this.client_secret); //все эти сложности из-за опасений насчет многопоточности и блокировок
+
+ lock scope="session" type="exclusive" timeout="1" {
+ session.auth = auth;
+ }
+ //request.auth = auth;
+ location(this.redirect_uri, false);
+ } catch (e) {echo('
****************** #e.message# : #e.detail# **************');}
+
+ } else { //отправляем браузер к IDP
+ strURL =idp.buildRedirectToAuthURL({"scope":'openid profile email',"state":request.auth_state,"allow_signup":false});
+ echo('self-made Auth link (idp) #strURL#
');
+ location(strUrl,false);
+ }
+
+ }
+
+ }
+
+ //request.logout_url = "";
+ if (structKeyExists(request, "auth") AND structKeyExists(request.auth, "id_token")) {
+ request.logout_url = "#this.logout_endpoint#?id_token_hint=#request.auth.id_token#&post_logout_redirect_uri=#this.redirect_uri#?logout";
+ // внимание, здесь подразумевается, что redirect_uri не содержит query_string
+ // после возвращения от IDP нам нужно будет зачистить сессию SSO, сделаем это по слову logout
}
@@ -344,7 +339,7 @@
--->
-
+
@@ -452,7 +447,7 @@
//https://keycloak.nubes.ru/realms/cloud/.well-known/openid-configuration тут ссылка
//https://keycloak.nubes.ru/realms/cloud/protocol/openid-connect/certs тут серт
//только надо его обрамить -----BEGIN CERTIFICATE-----
- function getIdpCertificate(certUrl) {
+ private function getIdpCertificate(certUrl) {
var httpService = new http();
httpService.setMethod( "get" );
@@ -475,6 +470,37 @@
} else {
throw (message="Cannot obtain IDP certificate, request failed", detail="Status_Code #status_code#");
}
+ }
+
+ private struct function parseIdpresponse(struct resp, string certificate, string jwt_secret) {
+
+ var auth = structNew("linked");
+ var data = deserializeJson(arguments.resp.content);
+ var jwt = new lib.jwt(arguments.jwt_secret);
+
+ auth.expires_in = data.expires_in;
+ auth.refresh_expires_in = data.refresh_expires_in;
+ auth.access_token = data.access_token;
+ auth.refresh_token = data.refresh_token;
+ auth.id_token = data.id_token;
+ auth.token_type = data.token_type;
+
+ //idp_response_content = structCopy(data); //for debug
+ var token_data = (jwt.decode(data.access_token, arguments.certificate,"RS256"));
+
+ auth.session_state = token_data.session_state;
+ auth.sid = token_data.sid;
+ auth.auth_time = token_data.auth_time;
+ auth.exp = token_data.exp;
+ auth.iat = token_data.iat;
+
+ auth.login = token_data.preferred_username;
+ auth.wz = token_data.ClientID;
+ auth.fullname = token_data.name;
+ auth.groups = token_data.groups;
+ //auth.token_data = structCopy(token_data); //for debug
+
+ return (auth);
}
diff --git a/json.cfm b/json.cfm
index 1a1df0b..f6e9f59 100644
--- a/json.cfm
+++ b/json.cfm
@@ -69,6 +69,7 @@
debug=#isDefined("DEBUG")#
/>