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")# />