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>