♻️ Refactoring code
This commit is contained in:
@@ -1,89 +1,95 @@
|
|||||||
const redirectUri = `${ window.location.origin }/`;
|
const redirectUri = `${window.location.origin}/`;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// GENERAL HELPER FUNCTIONS
|
// GENERAL HELPER FUNCTIONS
|
||||||
|
|
||||||
// Make a POST request and parse the response as JSON
|
// Make a POST request and parse the response as JSON
|
||||||
const sendPostRequest = async(url, params) => {
|
const sendPostRequest = async (url, params) => {
|
||||||
let body = Object.keys(params).map(key => key + '=' + params[key]).join('&');
|
let body = Object.keys(params)
|
||||||
|
.map(key => `${key}=${params[key]}`)
|
||||||
|
.join("&");
|
||||||
const options = {
|
const options = {
|
||||||
method: 'post',
|
method: "post",
|
||||||
headers: {
|
headers: {
|
||||||
'Content-type': 'application/x-www-form-urlencoded; charset=UTF-8'
|
"Content-type": "application/x-www-form-urlencoded; charset=UTF-8"
|
||||||
},
|
},
|
||||||
body
|
body
|
||||||
}
|
};
|
||||||
try {
|
try {
|
||||||
const response = await fetch(url, options);
|
const response = await fetch(url, options);
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
return data;
|
return data;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Request failed', err);
|
console.error("Request failed", err);
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
// Parse a query string into an object
|
// Parse a query string into an object
|
||||||
const parseQueryString = string => {
|
const parseQueryString = string => {
|
||||||
if(string == "") { return {}; }
|
if (string === "") {
|
||||||
let segments = string.split("&").map(s => s.split("=") );
|
return {};
|
||||||
|
}
|
||||||
|
let segments = string.split("&").map(s => s.split("="));
|
||||||
let queryString = {};
|
let queryString = {};
|
||||||
segments.forEach(s => queryString[s[0]] = s[1]);
|
segments.forEach(s => (queryString[s[0]] = s[1]));
|
||||||
return queryString;
|
return queryString;
|
||||||
}
|
};
|
||||||
// Get OAuth configuration from OpenID Discovery endpoint
|
// Get OAuth configuration from OpenID Discovery endpoint
|
||||||
const getTokenConfiguration = async endpoint => {
|
const getTokenConfiguration = async endpoint => {
|
||||||
const options = {
|
const options = {
|
||||||
method: 'GET',
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
'Content-type': 'application/json'
|
"Content-type": "application/json"
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
try {
|
try {
|
||||||
const response = await fetch(endpoint, options);
|
const response = await fetch(endpoint, options);
|
||||||
const config = await response.json();
|
const config = await response.json();
|
||||||
return config;
|
return config;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Request failed', err);
|
console.error("Request failed", err);
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// PKCE HELPER FUNCTIONS
|
// PKCE HELPER FUNCTIONS
|
||||||
|
|
||||||
// Generate a secure random string using the browser crypto functions
|
// Generate a secure random string using the browser crypto functions
|
||||||
const generateRandomString = () => {
|
const generateRandomString = () => {
|
||||||
var array = new Uint32Array(28);
|
const array = new Uint32Array(28);
|
||||||
window.crypto.getRandomValues(array);
|
window.crypto.getRandomValues(array);
|
||||||
return Array.from(array, dec => ('0' + dec.toString(16)).substr(-2)).join('');
|
return Array.from(array, dec => `0${dec.toString(16)}`.substr(-2)).join("");
|
||||||
}
|
};
|
||||||
// Calculate the SHA256 hash of the input text.
|
// Calculate the SHA256 hash of the input text.
|
||||||
// Returns a promise that resolves to an ArrayBuffer
|
// Returns a promise that resolves to an ArrayBuffer
|
||||||
const sha256 = plain => {
|
const sha256 = plain => {
|
||||||
const encoder = new TextEncoder();
|
const encoder = new TextEncoder();
|
||||||
const data = encoder.encode(plain);
|
const data = encoder.encode(plain);
|
||||||
return window.crypto.subtle.digest('SHA-256', data);
|
return window.crypto.subtle.digest("SHA-256", data);
|
||||||
}
|
};
|
||||||
// Base64-urlencodes the input string
|
// Base64-urlencodes the input string
|
||||||
const base64urlencode = str => {
|
const base64urlencode = (
|
||||||
// Convert the ArrayBuffer to string using Uint8 array to conver to what btoa accepts.
|
str // Convert the ArrayBuffer to string using Uint8 array to conver to what btoa accepts.
|
||||||
|
) =>
|
||||||
// btoa accepts chars only within ascii 0-255 and base64 encodes them.
|
// btoa accepts chars only within ascii 0-255 and base64 encodes them.
|
||||||
// Then convert the base64 encoded to base64url encoded
|
// Then convert the base64 encoded to base64url encoded
|
||||||
// (replace + with -, replace / with _, trim trailing =)
|
// (replace + with -, replace / with _, trim trailing =)
|
||||||
return btoa(String.fromCharCode.apply(null, new Uint8Array(str)))
|
btoa(String.fromCharCode.apply(null, new Uint8Array(str)))
|
||||||
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
.replace(/\+/g, "-")
|
||||||
}
|
.replace(/\//g, "_")
|
||||||
|
.replace(/=+$/, "");
|
||||||
// Return the base64-urlencoded sha256 hash for the PKCE challenge
|
// Return the base64-urlencoded sha256 hash for the PKCE challenge
|
||||||
const pkceChallengeFromVerifier = async(v) => {
|
const pkceChallengeFromVerifier = async v => {
|
||||||
let hashed = await sha256(v);
|
let hashed = await sha256(v);
|
||||||
return base64urlencode(hashed);
|
return base64urlencode(hashed);
|
||||||
}
|
};
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// OAUTH REQUEST
|
// OAUTH REQUEST
|
||||||
|
|
||||||
// Initiate PKCE Auth Code flow when requested
|
// Initiate PKCE Auth Code flow when requested
|
||||||
const tokenRequest = async({
|
const tokenRequest = async ({
|
||||||
oidcDiscoveryUrl,
|
oidcDiscoveryUrl,
|
||||||
grantType,
|
grantType,
|
||||||
authUrl,
|
authUrl,
|
||||||
@@ -91,85 +97,89 @@ const tokenRequest = async({
|
|||||||
clientId,
|
clientId,
|
||||||
scope
|
scope
|
||||||
}) => {
|
}) => {
|
||||||
|
|
||||||
// Check oauth configuration
|
// Check oauth configuration
|
||||||
if (oidcDiscoveryUrl !== '') {
|
if (oidcDiscoveryUrl !== "") {
|
||||||
const { authorization_endpoint, token_endpoint } = await getTokenConfiguration(oidcDiscoveryUrl);
|
const {
|
||||||
|
authorization_endpoint,
|
||||||
|
token_endpoint
|
||||||
|
} = await getTokenConfiguration(oidcDiscoveryUrl);
|
||||||
authUrl = authorization_endpoint;
|
authUrl = authorization_endpoint;
|
||||||
accessTokenUrl = token_endpoint;
|
accessTokenUrl = token_endpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store oauth information
|
// Store oauth information
|
||||||
localStorage.setItem('token_endpoint', accessTokenUrl);
|
localStorage.setItem("token_endpoint", accessTokenUrl);
|
||||||
localStorage.setItem('client_id', clientId);
|
localStorage.setItem("client_id", clientId);
|
||||||
|
|
||||||
// Create and store a random state value
|
// Create and store a random state value
|
||||||
const state = generateRandomString();
|
const state = generateRandomString();
|
||||||
localStorage.setItem('pkce_state', state);
|
localStorage.setItem("pkce_state", state);
|
||||||
|
|
||||||
// Create and store a new PKCE code_verifier (the plaintext random secret)
|
// Create and store a new PKCE code_verifier (the plaintext random secret)
|
||||||
const code_verifier = generateRandomString();
|
const code_verifier = generateRandomString();
|
||||||
localStorage.setItem('pkce_code_verifier', code_verifier);
|
localStorage.setItem("pkce_code_verifier", code_verifier);
|
||||||
|
|
||||||
// Hash and base64-urlencode the secret to use as the challenge
|
// Hash and base64-urlencode the secret to use as the challenge
|
||||||
const code_challenge = await pkceChallengeFromVerifier(code_verifier);
|
const code_challenge = await pkceChallengeFromVerifier(code_verifier);
|
||||||
|
|
||||||
// Build the authorization URL
|
// Build the authorization URL
|
||||||
const buildUrl = () => {
|
const buildUrl = () =>
|
||||||
return authUrl
|
`${authUrl + `?response_type=${grantType}`}&client_id=${encodeURIComponent(
|
||||||
+ `?response_type=${grantType}`
|
clientId
|
||||||
+ '&client_id='+encodeURIComponent(clientId)
|
)}&state=${encodeURIComponent(state)}&scope=${encodeURIComponent(
|
||||||
+ '&state='+encodeURIComponent(state)
|
scope
|
||||||
+ '&scope='+encodeURIComponent(scope)
|
)}&redirect_uri=${encodeURIComponent(
|
||||||
+ '&redirect_uri='+encodeURIComponent(redirectUri)
|
redirectUri
|
||||||
+ '&code_challenge='+encodeURIComponent(code_challenge)
|
)}&code_challenge=${encodeURIComponent(
|
||||||
+ '&code_challenge_method=S256'
|
code_challenge
|
||||||
;
|
)}&code_challenge_method=S256`;
|
||||||
}
|
|
||||||
|
|
||||||
// Redirect to the authorization server
|
// Redirect to the authorization server
|
||||||
window.location = buildUrl();
|
window.location = buildUrl();
|
||||||
}
|
};
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// OAUTH REDIRECT HANDLING
|
// OAUTH REDIRECT HANDLING
|
||||||
|
|
||||||
// Handle the redirect back from the authorization server and
|
// Handle the redirect back from the authorization server and
|
||||||
// get an access token from the token endpoint
|
// get an access token from the token endpoint
|
||||||
const oauthRedirect = async() => {
|
const oauthRedirect = async () => {
|
||||||
let tokenResponse = '';
|
let tokenResponse = "";
|
||||||
let q = parseQueryString(window.location.search.substring(1));
|
let q = parseQueryString(window.location.search.substring(1));
|
||||||
// Check if the server returned an error string
|
// Check if the server returned an error string
|
||||||
if(q.error) {
|
if (q.error) {
|
||||||
alert('Error returned from authorization server: '+q.error);
|
alert(`Error returned from authorization server: ${q.error}`);
|
||||||
}
|
}
|
||||||
// If the server returned an authorization code, attempt to exchange it for an access token
|
// If the server returned an authorization code, attempt to exchange it for an access token
|
||||||
if(q.code) {
|
if (q.code) {
|
||||||
// Verify state matches what we set at the beginning
|
// Verify state matches what we set at the beginning
|
||||||
if(localStorage.getItem('pkce_state') != q.state) {
|
if (localStorage.getItem("pkce_state") != q.state) {
|
||||||
alert('Invalid state');
|
alert("Invalid state");
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
// Exchange the authorization code for an access token
|
// Exchange the authorization code for an access token
|
||||||
tokenResponse = await sendPostRequest(localStorage.getItem('token_endpoint'), {
|
tokenResponse = await sendPostRequest(
|
||||||
grant_type: 'authorization_code',
|
localStorage.getItem("token_endpoint"),
|
||||||
code: q.code,
|
{
|
||||||
client_id: localStorage.getItem('client_id'),
|
grant_type: "authorization_code",
|
||||||
redirect_uri: redirectUri,
|
code: q.code,
|
||||||
code_verifier: localStorage.getItem('pkce_code_verifier')
|
client_id: localStorage.getItem("client_id"),
|
||||||
});
|
redirect_uri: redirectUri,
|
||||||
|
code_verifier: localStorage.getItem("pkce_code_verifier")
|
||||||
|
}
|
||||||
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(error.error+'\n\n'+error.error_description);
|
console.log(`${error.error}\n\n${error.error_description}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Clean these up since we don't need them anymore
|
// Clean these up since we don't need them anymore
|
||||||
localStorage.removeItem('pkce_state');
|
localStorage.removeItem("pkce_state");
|
||||||
localStorage.removeItem('pkce_code_verifier');
|
localStorage.removeItem("pkce_code_verifier");
|
||||||
localStorage.removeItem('token_endpoint');
|
localStorage.removeItem("token_endpoint");
|
||||||
localStorage.removeItem('client_id');
|
localStorage.removeItem("client_id");
|
||||||
return tokenResponse;
|
return tokenResponse;
|
||||||
}
|
}
|
||||||
return tokenResponse;
|
return tokenResponse;
|
||||||
}
|
};
|
||||||
|
|
||||||
export { tokenRequest, oauthRedirect }
|
export { tokenRequest, oauthRedirect };
|
||||||
|
|||||||
@@ -549,8 +549,8 @@ export default {
|
|||||||
this.$store.state.postwoman.settings.THEME_CLASS || "";
|
this.$store.state.postwoman.settings.THEME_CLASS || "";
|
||||||
// Load theme color data from settings, or use default color.
|
// Load theme color data from settings, or use default color.
|
||||||
let color = this.$store.state.postwoman.settings.THEME_COLOR || "#50fa7b";
|
let color = this.$store.state.postwoman.settings.THEME_COLOR || "#50fa7b";
|
||||||
let vibrant = this.$store.state.postwoman.settings.THEME_COLOR_VIBRANT;
|
let vibrant =
|
||||||
if (vibrant == null) vibrant = true;
|
this.$store.state.postwoman.settings.THEME_COLOR_VIBRANT || true;
|
||||||
document.documentElement.style.setProperty("--ac-color", color);
|
document.documentElement.style.setProperty("--ac-color", color);
|
||||||
document.documentElement.style.setProperty(
|
document.documentElement.style.setProperty(
|
||||||
"--act-color",
|
"--act-color",
|
||||||
|
|||||||
@@ -288,7 +288,10 @@
|
|||||||
</label>
|
</label>
|
||||||
<div v-if="queryFields.length > 0" class="tab">
|
<div v-if="queryFields.length > 0" class="tab">
|
||||||
<div v-for="field in queryFields" :key="field.name">
|
<div v-for="field in queryFields" :key="field.name">
|
||||||
<gql-field :gqlField="field" :jumpTypeCallback="handleJumpToType" />
|
<gql-field
|
||||||
|
:gqlField="field"
|
||||||
|
:jumpTypeCallback="handleJumpToType"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -304,7 +307,10 @@
|
|||||||
</label>
|
</label>
|
||||||
<div v-if="mutationFields.length > 0" class="tab">
|
<div v-if="mutationFields.length > 0" class="tab">
|
||||||
<div v-for="field in mutationFields" :key="field.name">
|
<div v-for="field in mutationFields" :key="field.name">
|
||||||
<gql-field :gqlField="field" :jumpTypeCallback="handleJumpToType" />
|
<gql-field
|
||||||
|
:gqlField="field"
|
||||||
|
:jumpTypeCallback="handleJumpToType"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -320,7 +326,10 @@
|
|||||||
</label>
|
</label>
|
||||||
<div v-if="subscriptionFields.length > 0" class="tab">
|
<div v-if="subscriptionFields.length > 0" class="tab">
|
||||||
<div v-for="field in subscriptionFields" :key="field.name">
|
<div v-for="field in subscriptionFields" :key="field.name">
|
||||||
<gql-field :gqlField="field" :jumpTypeCallback="handleJumpToType" />
|
<gql-field
|
||||||
|
:gqlField="field"
|
||||||
|
:jumpTypeCallback="handleJumpToType"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -335,8 +344,15 @@
|
|||||||
{{ $t("types") }}
|
{{ $t("types") }}
|
||||||
</label>
|
</label>
|
||||||
<div v-if="gqlTypes.length > 0" class="tab">
|
<div v-if="gqlTypes.length > 0" class="tab">
|
||||||
<div v-for="type in gqlTypes" :key="type.name" :id="`type_${type.name}`">
|
<div
|
||||||
<gql-type :gqlType="type" :jumpTypeCallback="handleJumpToType" />
|
v-for="type in gqlTypes"
|
||||||
|
:key="type.name"
|
||||||
|
:id="`type_${type.name}`"
|
||||||
|
>
|
||||||
|
<gql-type
|
||||||
|
:gqlType="type"
|
||||||
|
:jumpTypeCallback="handleJumpToType"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -572,13 +588,13 @@ export default {
|
|||||||
const target = document.getElementById(`type_${rootTypeName}`);
|
const target = document.getElementById(`type_${rootTypeName}`);
|
||||||
if (target) {
|
if (target) {
|
||||||
target.scrollIntoView({
|
target.scrollIntoView({
|
||||||
behavior: 'smooth'
|
behavior: "smooth"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
resolveRootType(type) {
|
resolveRootType(type) {
|
||||||
let t = type;
|
let t = type;
|
||||||
while (t.ofType != null) t = t.ofType;
|
while (t.ofType !== null) t = t.ofType;
|
||||||
return t;
|
return t;
|
||||||
},
|
},
|
||||||
copySchema() {
|
copySchema() {
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ export const state = () => ({
|
|||||||
export const mutations = {
|
export const mutations = {
|
||||||
applySetting({ settings }, setting) {
|
applySetting({ settings }, setting) {
|
||||||
if (
|
if (
|
||||||
setting == null ||
|
setting === null ||
|
||||||
!(setting instanceof Array) ||
|
!(setting instanceof Array) ||
|
||||||
setting.length !== 2
|
setting.length !== 2
|
||||||
) {
|
) {
|
||||||
|
|||||||
Reference in New Issue
Block a user