charts/osdu-developer-auth/templates/config-map-spa.yaml (224 lines of code) (raw):

{{- $namespace := .Release.Namespace }} {{- $clientId := .Values.azure.appId }} {{- $tenantId := .Values.azure.tenantId }} --- apiVersion: v1 kind: ConfigMap metadata: name: osdu-auth-spa-html namespace: {{ $namespace }} data: index.html: | <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta http-equiv="x-ua-compatible" content="ie=edge"> <title>OAuth Login</title> <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.2/css/all.css"> <link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet"> <style type="text/css"> textarea { margin-bottom: 5px; } </style> </head> <body> <div id="app"> <form id="oauth-form" @submit.prevent="handleAuthorize" class="text-center border border-light p-5" action="#!"> <p class="h2 mb-4">Microsoft Identity Platform (v2.0)</p> <div class="form-group"> <div class="form-row"> <div class="form-group col-md-6"> <label class="float-left" for="clientId">ClientId</label> <input type="text" class="form-control" id="clientId" v-model="clientId" readonly> </div> <div class="form-group col-md-6"> <label class="float-left" for="tenantId">TenantId</label> <input type="text" class="form-control" id="tenantId" v-model="tenantId" readonly> </div> </div> <div class="form-row"> <div class="col-md-6 mb-3"> <label class="float-left" for="redirectUrl">RedirectUrl</label> <input type="text" class="form-control" id="redirectUrl" v-model="redirectUrl" readonly> </div> <div class="col-md-6 mb-3"> <label class="float-left" for="scope">Scope</label> <input type="text" class="form-control" id="scope" v-model="scope" readonly> </div> </div> <div class="form-row"> <div class="col-md-12 mb-1"> <a v-if="!authorizationCode && !refreshToken" @click="handleAuthorize" class="btn btn-primary float-right">Authorize</a> <button @click="clearTokens" class="btn btn-danger float-left">Clear</button> <button v-if="authorizationCode || refreshToken" @click="getTokens" class="btn btn-success float-right">Get Tokens</button> </div> </div> </div> <hr /> <div class="form-group shadow-textarea"> <div class="form-row"> <div class="col-md-4 mb-4"> <label class="float-left" for="authorizationCode">Authorization Code</label> <textarea id="authorizationCode" v-model="authorizationCode" class="form-control z-depth-1" rows="5"></textarea> </div> <div class="col-md-4 mb-4"> <label class="float-left" for="accessToken">Access Token</label> <textarea id="accessToken" v-model="accessToken" class="form-control z-depth-1" rows="5"></textarea> <a @click="decodeAccess()" class="btn btn-info btn-sm float-right" v-if="accessToken">Decode</a> </div> <div class="col-md-4 mb-4"> <label class="float-left" for="refreshToken">Refresh Token</label> <textarea id="refreshToken" v-model="refreshToken" class="form-control z-depth-1" rows="5"></textarea> </div> </div> </div> </form> </div> <!-- SCRIPTS --> <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> <!-- Axios script --> <script> function base64urlencode(source) { // Encode the source string into base64url format return btoa(String.fromCharCode.apply(null, new Uint8Array(source))) .replace(/\+/g, '-') .replace(/\//g, '_') .replace(/=+$/, ''); } async function generateCodeVerifier() { const array = new Uint8Array(32); window.crypto.getRandomValues(array); return base64urlencode(array); } async function generateCodeChallenge(verifier) { const encoder = new TextEncoder(); const data = encoder.encode(verifier); const digest = await window.crypto.subtle.digest('SHA-256', data); return base64urlencode(digest); } var app = new Vue({ el: '#app', data: { tenantId: '{{ $tenantId }}', clientId: '{{ $clientId }}', redirectUrl: window.location.origin + '/auth/spa/', scope: '{{ $clientId }}/.default openid profile offline_access', codeVerifier: '', codeChallenge: '', authorizationCode: null, accessToken: null, refreshToken: localStorage.getItem('refresh_token') || null, }, computed: { authorizeUrl: function () { return "https://login.microsoftonline.com/" + this.tenantId + "/oauth2/v2.0/authorize?" + "client_id=" + this.clientId + "&response_type=code" + "&redirect_uri=" + this.redirectUrl + "&response_mode=query" + "&scope=" + this.scope + "&state=12345" + "&nonce=dummy123" + "&code_challenge=" + this.codeChallenge + "&code_challenge_method=S256"; } }, methods: { async getAuthCode() { try { const storedCodeVerifier = localStorage.getItem('code_verifier'); const response = await axios.post('https://login.microsoftonline.com/' + this.tenantId + '/oauth2/v2.0/token', new URLSearchParams({ client_id: this.clientId, grant_type: 'authorization_code', code: this.authorizationCode, redirect_uri: this.redirectUrl, code_verifier: storedCodeVerifier })); // Update tokens this.accessToken = response.data.access_token; this.refreshToken = response.data.refresh_token; this.authorizationCode = null; // Clear the authorization code after exchange // Store refresh token in localStorage localStorage.setItem('refresh_token', this.refreshToken); localStorage.setItem('access_token', this.accessToken); // Clear the authorization code from the URL history.replaceState({}, document.title, window.location.pathname); } catch (error) { console.error('Error exchanging authorization code for tokens:', error); // Optionally, handle error and provide feedback to the user } }, async refreshAccessToken() { try { const response = await axios.post('https://login.microsoftonline.com/' + this.tenantId + '/oauth2/v2.0/token', new URLSearchParams({ client_id: this.clientId, grant_type: 'refresh_token', refresh_token: this.refreshToken, redirect_uri: this.redirectUrl })); // Update tokens this.accessToken = response.data.access_token; this.refreshToken = response.data.refresh_token; // Store refresh token in localStorage localStorage.setItem('refresh_token', this.refreshToken); localStorage.setItem('access_token', this.accessToken); } catch (error) { console.error('Error refreshing access token:', error); // Optionally, handle error and provide feedback to the user } }, async getTokens() { if (this.authorizationCode) { await this.getAuthCode(); } else if (this.refreshToken) { await this.refreshAccessToken(); } }, async handleAuthorize() { if (this.refreshToken) { await this.refreshAccessToken(); } else { this.codeVerifier = await generateCodeVerifier(); this.codeChallenge = await generateCodeChallenge(this.codeVerifier); localStorage.setItem('code_verifier', this.codeVerifier); window.location.href = this.authorizeUrl; } }, clearTokens: function () { this.accessToken = null; this.refreshToken = null; this.authorizationCode = null; localStorage.removeItem('refresh_token'); localStorage.removeItem('access_token'); window.location.href = window.location.origin + '/auth/spa/'; }, decodeAccess: function () { var decodeUrl = "https://jwt.ms/#id_token=" + this.accessToken; window.open(decodeUrl, "_blank"); } }, async beforeMount() { const urlParams = new URLSearchParams(window.location.search); this.authorizationCode = urlParams.get('code'); this.accessToken = localStorage.getItem('access_token'); this.refreshToken = localStorage.getItem('refresh_token'); if (this.accessToken && this.refreshToken) { // Clear the authorization code this.authorizationCode = null; localStorage.removeItem('code_verifier'); history.replaceState({}, document.title, window.location.pathname); } if (this.authorizationCode) { this.codeVerifier = localStorage.getItem('code_verifier'); } } }); </script> </body> </html>