@@ -27,118 +27,120 @@ From https://github.com/curityio/pkce-javascript-example
2727 <title >OAuth2 PKCE flow</title >
2828 </head >
2929 <body >
30- <script >
31-
32- var oauth2Config = null ;
33-
34- var xhrOAuth2C = new XMLHttpRequest ();
35-
36- xhrOAuth2C .onload = function () {
37- var response = xhrOAuth2C .response ;
38-
39- if (xhrOAuth2C .status == 200 ) {
40- oauthConfig = response;
41- } else {
42- alert (" Error: " + response .error_description + " (" + response .error + " )" );
43- }
44- };
45- xhrOAuth2C .responseType = ' json' ;
46- xhrOAuth2C .open (" GET" , ' /oauth2configservice' , true );
47- // xhrOAuth2C.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
48- xhrOAuth2C .send ();
49-
50- const authorizeEndpoint = oauth2Config .authorizeUrl ;
51- const tokenEndpoint = oauth2Config .accessTokenUrl ;
52- const clientId = oauth2Config .clientId ;
53- const redirectUri = oauth2Config .redirectUrl ;
54-
55- if (window .location .search ) {
56- var args = new URLSearchParams (window .location .search );
57- var code = args .get (" code" );
58- var state = args .get (" state" );
59-
60- if (code) {
61- if (window .sessionStorage .getItem (" state" ) !== state){
62- throw Error (" Probable session hijacking attack!" );
63- }
64-
65- var xhr = new XMLHttpRequest ();
66-
67- xhr .onload = function () {
68- var response = xhr .response ;
69-
70- if (xhr .status == 200 ) {
71- // storing id_token for later usage (on logout)
72- window .sessionStorage .setItem (" id_token" , response .id_token );
73-
74- var lastRedirectUri = window .location .href .split (' ?' )[0 ];
75- var args = new URLSearchParams ({
76- PAGE : " LoginPage" ,
77- NEW_SESSION : " TRUE" ,
78- access_token: response .access_token
79- });
80- window .location = lastRedirectUri + " ?" + args;
81- } else {
82- alert (" Error: " + response .error_description + " (" + response .error + " )" );
83- }
84-
85- document .getElementById (" result" ).innerHTML = message;
86- };
87- xhr .responseType = ' json' ;
88- xhr .open (" POST" , tokenEndpoint, true );
89- xhr .setRequestHeader (' Content-type' , ' application/x-www-form-urlencoded' );
90- xhr .send (new URLSearchParams ({
91- client_id: clientId,
92- code_verifier: window .sessionStorage .getItem (" code_verifier" ),
93- grant_type: " authorization_code" ,
94- redirect_uri: redirectUri,
95- code: code,
96- state: state
97- }));
98- } else {
99- startOauth2Flow ();
30+ <script >
31+ start ();
32+
33+ async function start () {
34+ if (window .location .search ) {
35+ var args = new URLSearchParams (window .location .search );
36+ var code = args .get (" code" );
37+ var state = args .get (" state" );
38+ if (code) {
39+ const oauth2Config = await fetchConfig ();
40+ if (window .sessionStorage .getItem (" state" ) !== state){
41+ throw Error (" Probable session hijacking attack!" );
42+ }
43+ fetch (oauth2Config .accessTokenUrl , {
44+ method: ' POST' ,
45+ headers: {
46+ ' Content-Type' : ' application/x-www-form-urlencoded'
47+ },
48+ body: new URLSearchParams ({
49+ client_id: oauth2Config .clientId ,
50+ code_verifier: window .sessionStorage .getItem (" code_verifier" ),
51+ grant_type: " authorization_code" ,
52+ redirect_uri: oauth2Config .redirectUrl ,
53+ code: code,
54+ state: state
55+ })
56+ })
57+ .then (response => response .json ().then (data => ({ status: response .status , body: data })))
58+ .then (({ status, body }) => {
59+ if (status === 200 ) {
60+ // storing id_token for later usage (on logout)
61+ window .sessionStorage .setItem (" id_token" , body .id_token );
62+
63+ const lastRedirectUri = window .location .href .split (' ?' )[0 ];
64+ const args = new URLSearchParams ({
65+ PAGE : " LoginPage" ,
66+ NEW_SESSION : " TRUE" ,
67+ access_token: body .access_token
68+ });
69+
70+ window .location = lastRedirectUri + " ?" + args;
71+ } else {
72+ alert (` Error: ${ body .error_description } (${ body .error } )` );
73+ }
74+
75+ })
76+ .catch (error => {
77+ console .error (" Errore nella richiesta fetch:" , error);
78+ });
79+
80+ } else {
81+ startOauth2Flow ();
82+ }
10083 }
10184 }
102-
103- function startOauth2Flow () {
104- var state = generateRandomString (64 );
105- var codeVerifier = generateRandomString (128 );
106-
107- generateCodeChallenge (codeVerifier).then (function (codeChallenge ) {
108- window .sessionStorage .setItem (" state" , state);
109- window .sessionStorage .setItem (" code_verifier" , codeVerifier);
110-
111- var args = new URLSearchParams ({
112- response_type: " code" ,
113- client_id: clientId,
114- code_challenge_method: " S256" ,
115- code_challenge: codeChallenge,
116- state: state,
117- redirect_uri: redirectUri,
118- scope: " openid profile"
119- });
120- window .location = authorizeEndpoint + " ?" + args;
121- });
85+
86+ async function fetchConfig () {
87+ const response = await fetch (' /knowage/restful-services/oauth2configservice' , {
88+ method: ' GET'
89+ })
90+
91+ if (! response .ok ) {
92+ throw new Error (" Errore nella chiamata oauth2configservice" );
93+ }
94+ const config = await response .json ();
95+ return config;
12296 }
123-
124- async function generateCodeChallenge (codeVerifier ) {
125- var digest = await crypto .subtle .digest (" SHA-256" ,
126- new TextEncoder ().encode (codeVerifier));
127-
128- return btoa (String .fromCharCode (... new Uint8Array (digest)))
129- .replace (/ =/ g , ' ' ).replace (/ \+ / g , ' -' ).replace (/ \/ / g , ' _' )
97+
98+ async function startOauth2Flow () {
99+ const oauth2Config = await fetchConfig ();
100+ const state = generateRandomString (64 );
101+ const verifier = generateRandomString (128 );
102+ const challenge = await generateCodeChallenge (verifier);
103+ sessionStorage .setItem (" state" , state);
104+ sessionStorage .setItem (" code_verifier" , verifier);
105+ var args = new URLSearchParams ({
106+ response_type: " code" ,
107+ client_id: oauth2Config .clientId ,
108+ code_challenge_method: " S256" ,
109+ code_challenge: challenge,
110+ state: state,
111+ redirect_uri: oauth2Config .redirectUrl ,
112+ scope: " openid profile"
113+ });
114+ window .location = oauth2Config .authorizeUrl + " ?" + args;
115+
130116 }
117+
131118
132- function generateRandomString (length ) {
133- var text = " " ;
134- var possible = " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" ;
135-
136- for (var i = 0 ; i < length; i++ ) {
137- text += possible .charAt (Math .floor (Math .random () * possible .length ));
138- }
119+ // Funzione per generare una stringa casuale (code_verifier)
120+ function generateRandomString (length ) {
121+ const charset = ' ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~' ;
122+ let result = ' ' ;
123+ const array = new Uint8Array (length);
124+ window .crypto .getRandomValues (array);
125+ for (let i = 0 ; i < array .length ; i++ ) {
126+ result += charset[array[i] % charset .length ];
127+ }
128+ return result;
129+ }
130+
131+
132+ // Funzione per generare il code_challenge (SHA256 + base64url)
133+ async function generateCodeChallenge (codeVerifier ) {
134+ const encoder = new TextEncoder ();
135+ const data = encoder .encode (codeVerifier);
136+ const digest = await window .crypto .subtle .digest (' SHA-256' , data);
137+ const base64url = btoa (String .fromCharCode (... new Uint8Array (digest)))
138+ .replace (/ \+ / g , ' -' )
139+ .replace (/ \/ / g , ' _' )
140+ .replace (/ =+ $ / , ' ' );
141+ return base64url;
142+ }
139143
140- return text;
141- }
142144
143145 </script >
144146 </body >
0 commit comments