1+ <!DOCTYPE html>
2+ < html lang ="en ">
3+
4+ < head >
5+ < meta charset ="UTF-8 ">
6+ < meta name ="viewport " content ="width=device-width, initial-scale=1.0 ">
7+ < title > OIDC PKCE Authentication</ title >
8+ < script src ="https://cdnjs.cloudflare.com/ajax/libs/jsSHA/3.3.1/sha.min.js "> </ script >
9+ </ head >
10+
11+ < body >
12+ < button id ="loginButton "> Login</ button >
13+ < button id ="logoutButton "> Logout</ button >
14+ < div id ="userInfo "> </ div >
15+
16+ < script >
17+ const clientId = 'openeo-public' ;
18+ const redirectUri = window . location . origin ;
19+ console . log ( "Using Redirect URI: " + redirectUri )
20+ const scope = 'openid profile email' ;
21+ const oidcConfigUrl = 'https://auth.test.eoepca.org/realms/eoepca/.well-known/openid-configuration' ;
22+ let authEndpoint , tokenEndpoint , userInfoEndpoint ;
23+
24+ async function fetchOidcConfig ( ) {
25+ const response = await fetch ( oidcConfigUrl ) ;
26+ const oidcConfig = await response . json ( ) ;
27+ authEndpoint = oidcConfig . authorization_endpoint ;
28+ tokenEndpoint = oidcConfig . token_endpoint ;
29+ userInfoEndpoint = oidcConfig . userinfo_endpoint ;
30+ }
31+
32+ function generateRandomString ( length ) {
33+ const charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' ;
34+ let randomString = '' ;
35+ for ( let i = 0 ; i < length ; i ++ ) {
36+ const randomIndex = Math . floor ( Math . random ( ) * charset . length ) ;
37+ randomString += charset . charAt ( randomIndex ) ;
38+ }
39+ return randomString ;
40+ }
41+
42+ function base64UrlEncode ( str ) {
43+ return btoa ( str ) . replace ( / \+ / g, '-' ) . replace ( / \/ / g, '_' ) . replace ( / = + $ / , '' ) ;
44+ }
45+
46+ function sha256 ( plain ) {
47+ const shaObj = new jsSHA ( 'SHA-256' , 'TEXT' ) ;
48+ shaObj . update ( plain ) ;
49+ const hash = shaObj . getHash ( 'BYTES' ) ;
50+ return hash ;
51+ }
52+
53+ async function login ( ) {
54+ const codeVerifier = generateRandomString ( 128 ) ;
55+ sessionStorage . setItem ( 'codeVerifier' , codeVerifier ) ;
56+
57+ const codeChallenge = base64UrlEncode ( sha256 ( codeVerifier ) ) ;
58+ const state = generateRandomString ( 16 ) ;
59+
60+ await fetchOidcConfig ( ) ;
61+
62+ const authUrl = `${ authEndpoint } ?response_type=code&client_id=${ clientId } &redirect_uri=${ redirectUri } &scope=${ scope } &code_challenge=${ codeChallenge } &code_challenge_method=S256&state=${ state } ` ;
63+ window . location = authUrl ;
64+ }
65+
66+ async function handleCallback ( ) {
67+ const urlParams = new URLSearchParams ( window . location . search ) ;
68+ const code = urlParams . get ( 'code' ) ;
69+ const state = urlParams . get ( 'state' ) ;
70+ const codeVerifier = sessionStorage . getItem ( 'codeVerifier' ) ;
71+
72+ if ( code ) {
73+ await fetchOidcConfig ( ) ;
74+
75+ const tokenResponse = await fetch ( tokenEndpoint , {
76+ method : 'POST' ,
77+ headers : {
78+ 'Content-Type' : 'application/x-www-form-urlencoded'
79+ } ,
80+ body : new URLSearchParams ( {
81+ grant_type : 'authorization_code' ,
82+ code : code ,
83+ client_id : clientId ,
84+ redirect_uri : redirectUri ,
85+ code_verifier : codeVerifier
86+ } )
87+ } ) ;
88+ const tokenData = await tokenResponse . json ( ) ;
89+ sessionStorage . setItem ( 'accessToken' , tokenData . access_token ) ;
90+ fetchUserInfo ( tokenData . access_token ) ;
91+ }
92+ }
93+
94+ async function fetchUserInfo ( accessToken ) {
95+ const userInfoResponse = await fetch ( userInfoEndpoint , {
96+ headers : {
97+ 'Authorization' : `Bearer ${ accessToken } `
98+ }
99+ } ) ;
100+ const userInfo = await userInfoResponse . json ( ) ;
101+ document . getElementById ( 'userInfo' ) . innerText = `Hello, ${ userInfo . name } ` ;
102+ }
103+
104+ function logout ( ) {
105+ sessionStorage . removeItem ( 'accessToken' ) ;
106+ document . getElementById ( 'userInfo' ) . innerText = '' ;
107+ }
108+
109+ document . getElementById ( 'loginButton' ) . addEventListener ( 'click' , login ) ;
110+ document . getElementById ( 'logoutButton' ) . addEventListener ( 'click' , logout ) ;
111+
112+ window . onload = handleCallback ;
113+ </ script >
114+ </ body >
115+
116+ </ html >
0 commit comments