From d24b917c173a7077ea96e4225b9c63b4285c54ca Mon Sep 17 00:00:00 2001 From: Abdul Rifqi Al Abqary Date: Mon, 6 Jan 2020 14:51:47 +0900 Subject: [PATCH 1/9] added oauth token ui --- lang/en-US.js | 16 ++- package-lock.json | 2 +- pages/index.vue | 335 ++++++++++++++++++++++++++++++++++++++++++++- store/mutations.js | 4 + store/state.js | 10 ++ 5 files changed, 360 insertions(+), 7 deletions(-) diff --git a/lang/en-US.js b/lang/en-US.js index 317b9718e..280da3e56 100644 --- a/lang/en-US.js +++ b/lang/en-US.js @@ -86,5 +86,19 @@ export default { connect: "Connect", disconnect: "Disconnect", start: "Start", - stop: "Stop" + stop: "Stop", + access_token: "Access Token", + get_token: "Get New Token", + manage_token: "Manage Access Token", + save_token: "Save Access Token", + use_token: "Use Saved Token", + request_token: "Request Token", + save_token_request: "Save Token Request", + token_name: "Token Name", + oidc_discovery_url: "OIDC Discovery URL", + auth_url: "Auth URL", + access_token_url: "Access Token URL", + client_id: "Client ID", + scope: "Scope", + state: "State", }; diff --git a/package-lock.json b/package-lock.json index a17a13373..2b47598bf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "postwoman", - "version": "1.0.0", + "version": "1.5.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/pages/index.vue b/pages/index.vue index 0549a247f..63c1015e6 100644 --- a/pages/index.vue +++ b/pages/index.vue @@ -388,11 +388,29 @@
@@ -404,6 +422,118 @@
+ + + + + + + + + @@ -805,6 +935,86 @@
+ + +
+
    +
  • +
    +

    {{ $t("save_token_request") }}

    +
    + +
    +
    +
  • +
+
+
+
    +
  • + +
  • +
+
+
+
+ + + + + +
+
+
+ + +
+
    +
  • +
    +

    {{ $t("manage_token") }}

    +
    + +
    +
    +
  • +
+
+
+
    +
  • + +
  • +
+
+
+
+ + + + + +
+
+
@@ -901,6 +1111,9 @@ export default { previewEnabled: false, paramsWatchEnabled: true, expandResponse: false, + showTokenList: false, + showTokenRequest: false, + showTokenRequestList: false, /** * These are content types that can be automatically @@ -1208,6 +1421,70 @@ export default { this.$store.commit("setState", { value, attribute: "bearerToken" }); } }, + token: { + get() { + return this.$store.oauth2.token; + }, + set(value) { + this.$store.commit("setOauth2", { value, attribute: "token" }); + } + }, + accessTokenName: { + get() { + return this.$store.state.oauth2.accessTokenName; + }, + set(value) { + this.$store.commit("setOauth2", { value, attribute: "accessTokenName" }); + } + }, + oidcDiscoveryUrl: { + get() { + return this.$store.state.oauth2.oidcDiscoveryUrl; + }, + set(value) { + this.$store.commit("setOauth2", { value, attribute: "oidcDiscoveryUrl" }); + } + }, + authUrl: { + get() { + return this.$store.state.oauth2.authUrl; + }, + set(value) { + this.$store.commit("setOauth2", { value, attribute: "authUrl" }); + } + }, + accessTokenUrl: { + get() { + return this.$store.state.oauth2.accessTokenUrl; + }, + set(value) { + this.$store.commit("setOauth2", { value, attribute: "accessTokenUrl" }); + } + }, + clientId: { + get() { + return this.$store.state.oauth2.clientId; + }, + set(value) { + this.$store.commit("setOauth2", { value, attribute: "clientId" }); + } + }, + scope: { + get() { + return this.$store.state.oauth2.scope; + }, + set(value) { + this.$store.commit("setOauth2", { value, attribute: "scope" }); + } + }, + state: { + get() { + return this.$store.state.oauth2.state; + }, + set(value) { + this.$store.commit("setOauth2", { value, attribute: "state" }); + } + }, headers: { get() { return this.$store.state.request.headers; @@ -2045,6 +2322,7 @@ export default { this.httpUser = ""; this.httpPassword = ""; this.bearerToken = ""; + this.showTokenRequest = false; break; case "headers": this.headers = []; @@ -2052,6 +2330,14 @@ export default { case "parameters": this.params = []; break; + case "access_token": + this.accessTokenName = ""; + this.oidcDiscoveryUrl = ""; + this.authUrl = ""; + this.accessTokenUrl = ""; + this.clientId = ""; + this.scope = ""; + break; default: (this.label = ""), (this.method = "GET"), @@ -2150,6 +2436,45 @@ export default { icon: "attach_file" }); } + }, + async handleAccessTokenRequest(){ + const tokenReqParams = { + grantType: "code", + oidcDiscoveryUrl: this.oidcDiscoveryUrl, + authUrl: this.authUrl, + accessTokenUrl: this.accessTokenUrl, + clientId: this.clientId, + clientSecret: this.clientSecret, + scope: this.scope, + clientAuth: this.clientAuth + }; + await tokenRequest(tokenReqParams); + }, + async oauthRedirectReq() { + let tokenInfo = await oauthRedirect(); + if(tokenInfo.hasOwnProperty('access_token')) { + this.accessToken = tokenInfo.access_token; + } + }, + saveToken(){ + try { + this.$toast.info("Access token saved"); + this.showTokenList = false; + } catch (e) { + this.$toast.error(e, { + icon: "code" + }); + } + }, + saveTokenRequest(){ + try { + this.$toast.info("Token request saved"); + this.showTokenRequestList = false; + } catch (e) { + this.$toast.error(e, { + icon: "code" + }); + } } }, mounted() { diff --git a/store/mutations.js b/store/mutations.js index 61bfd4757..67d17b696 100644 --- a/store/mutations.js +++ b/store/mutations.js @@ -85,5 +85,9 @@ export default { setValueBodyParams({ request }, { index, value }) { request.bodyParams[index].value = value; + }, + + setOauth2({ oauth2 }, { attribute, value }) { + oauth2[attribute] = value; } }; diff --git a/store/state.js b/store/state.js index 3f8a25da1..4e9e06c54 100644 --- a/store/state.js +++ b/store/state.js @@ -22,5 +22,15 @@ export default () => ({ headers: [], variables: [], query: "" + }, + oauth2: { + token: [], + accessTokenName: "", + oidcDiscoveryUrl: "", + authUrl: "", + accessTokenUrl: "", + clientId: "", + scope: "", + state: "", } }); From 5377c69b407c65fb8c1d522ccc31be73ee5a450d Mon Sep 17 00:00:00 2001 From: Abdul Rifqi Al Abqary Date: Mon, 6 Jan 2020 15:06:50 +0900 Subject: [PATCH 2/9] add oauth handler --- assets/js/oauth.js | 150 +++++++++++++++++++++++++++++++++++++++++++++ pages/index.vue | 4 +- store/state.js | 3 +- 3 files changed, 154 insertions(+), 3 deletions(-) create mode 100644 assets/js/oauth.js diff --git a/assets/js/oauth.js b/assets/js/oauth.js new file mode 100644 index 000000000..5925b943c --- /dev/null +++ b/assets/js/oauth.js @@ -0,0 +1,150 @@ +const redirectUri = `${ window.location.origin }/`; + +////////////////////////////////////////////////////////////////////// +// GENERAL HELPER FUNCTIONS + +// Make a POST request and parse the response as JSON +const sendPostRequest = async(url, params) => { + let body = Object.keys(params).map(key => key + '=' + params[key]).join('&'); + const options = { + method: 'post', + headers: { + 'Content-type': 'application/x-www-form-urlencoded; charset=UTF-8' + }, + body + } + try { + const response = await fetch(url, options); + const data = await response.json(); + return data; + } catch (err) { + console.error('Request failed', err); + throw err; + } +} +// Parse a query string into an object +const parseQueryString = string => { + if(string == "") { return {}; } + let segments = string.split("&").map(s => s.split("=") ); + let queryString = {}; + segments.forEach(s => queryString[s[0]] = s[1]); + return queryString; +} + +////////////////////////////////////////////////////////////////////// +// PKCE HELPER FUNCTIONS + +// Generate a secure random string using the browser crypto functions +const generateRandomString = () => { + var array = new Uint32Array(28); + window.crypto.getRandomValues(array); + return Array.from(array, dec => ('0' + dec.toString(16)).substr(-2)).join(''); +} +// Calculate the SHA256 hash of the input text. +// Returns a promise that resolves to an ArrayBuffer +const sha256 = plain => { + const encoder = new TextEncoder(); + const data = encoder.encode(plain); + return window.crypto.subtle.digest('SHA-256', data); +} +// Base64-urlencodes the input string +const base64urlencode = 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. + // Then convert the base64 encoded to base64url encoded + // (replace + with -, replace / with _, trim trailing =) + return btoa(String.fromCharCode.apply(null, new Uint8Array(str))) + .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); +} +// Return the base64-urlencoded sha256 hash for the PKCE challenge +const pkceChallengeFromVerifier = async(v) => { + let hashed = await sha256(v); + return base64urlencode(hashed); +} + +////////////////////////////////////////////////////////////////////// +// OAUTH REQUEST + +// Initiate PKCE Auth Code flow when requested +const tokenRequest = async({ + grantType, + authUrl, + accessTokenUrl, + clientId, + scope +}) => { + + // Store oauth information + localStorage.setItem('token_endpoint', accessTokenUrl); + localStorage.setItem('client_id', clientId); + + // Create and store a random state value + const state = generateRandomString(); + localStorage.setItem('pkce_state', state); + + // Create and store a new PKCE code_verifier (the plaintext random secret) + const code_verifier = generateRandomString(); + localStorage.setItem('pkce_code_verifier', code_verifier); + + // Hash and base64-urlencode the secret to use as the challenge + const code_challenge = await pkceChallengeFromVerifier(code_verifier); + + // Build the authorization URL + const buildUrl = () => { + return authUrl + + `?response_type=${grantType}` + + '&client_id='+encodeURIComponent(clientId) + + '&state='+encodeURIComponent(state) + + '&scope='+encodeURIComponent(scope) + + '&redirect_uri='+encodeURIComponent(redirectUri) + + '&code_challenge='+encodeURIComponent(code_challenge) + + '&code_challenge_method=S256' + ; + } + + // Redirect to the authorization server + window.location = buildUrl(); +} + +////////////////////////////////////////////////////////////////////// +// OAUTH REDIRECT HANDLING + +// Handle the redirect back from the authorization server and +// get an access token from the token endpoint +const oauthRedirect = async() => { + let tokenResponse = ''; + let q = parseQueryString(window.location.search.substring(1)); + // Check if the server returned an error string + if(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(q.code) { + // Verify state matches what we set at the beginning + if(localStorage.getItem('pkce_state') != q.state) { + alert('Invalid state'); + } else { + try { + // Exchange the authorization code for an access token + tokenResponse = await sendPostRequest(localStorage.getItem('token_endpoint'), { + grant_type: 'authorization_code', + code: q.code, + client_id: localStorage.getItem('client_id'), + redirect_uri: redirectUri, + code_verifier: localStorage.getItem('pkce_code_verifier') + }); + } catch (err) { + console.log(error.error+'\n\n'+error.error_description); + } + } + // Clean these up since we don't need them anymore + localStorage.removeItem('pkce_state'); + localStorage.removeItem('pkce_code_verifier'); + localStorage.removeItem('token_endpoint'); + localStorage.removeItem('client_id'); + return tokenResponse; + } + return tokenResponse; +} + +export { tokenRequest, oauthRedirect } diff --git a/pages/index.vue b/pages/index.vue index 63c1015e6..e0300da51 100644 --- a/pages/index.vue +++ b/pages/index.vue @@ -1028,6 +1028,7 @@ import parseCurlCommand from "../assets/js/curlparser.js"; import getEnvironmentVariablesFromScript from "../functions/preRequest"; import parseTemplateString from "../functions/templating"; import AceEditor from "../components/ace-editor"; +import { tokenRequest, oauthRedirect } from "../assets/js/oauth"; const statusCategories = [ { @@ -2477,7 +2478,7 @@ export default { } } }, - mounted() { + async mounted() { this.observeRequestButton(); this._keyListener = function(e) { if (e.key === "g" && (e.ctrlKey || e.metaKey)) { @@ -2495,6 +2496,7 @@ export default { } }; document.addEventListener("keydown", this._keyListener.bind(this)); + await this.oauthRedirectReq(); }, created() { this.urlExcludes = this.$store.state.postwoman.settings.URL_EXCLUDES || { diff --git a/store/state.js b/store/state.js index 4e9e06c54..ed9467f1b 100644 --- a/store/state.js +++ b/store/state.js @@ -30,7 +30,6 @@ export default () => ({ authUrl: "", accessTokenUrl: "", clientId: "", - scope: "", - state: "", + scope: "" } }); From b98d9074bbefc760988cda8f4a4ea979cc991d02 Mon Sep 17 00:00:00 2001 From: Abdul Rifqi Al Abqary Date: Mon, 6 Jan 2020 15:55:14 +0900 Subject: [PATCH 3/9] fix token placement --- pages/index.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/index.vue b/pages/index.vue index e0300da51..765382022 100644 --- a/pages/index.vue +++ b/pages/index.vue @@ -2454,7 +2454,7 @@ export default { async oauthRedirectReq() { let tokenInfo = await oauthRedirect(); if(tokenInfo.hasOwnProperty('access_token')) { - this.accessToken = tokenInfo.access_token; + this.bearerToken = tokenInfo.access_token; } }, saveToken(){ From 2e7e40c4cc30de890b898c19b3975b55c27d027a Mon Sep 17 00:00:00 2001 From: Abdul Rifqi Al Abqary Date: Mon, 6 Jan 2020 16:15:58 +0900 Subject: [PATCH 4/9] display error & disable input based on user input --- pages/index.vue | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/pages/index.vue b/pages/index.vue index 765382022..750212216 100644 --- a/pages/index.vue +++ b/pages/index.vue @@ -469,6 +469,7 @@
  • Date: Mon, 6 Jan 2020 16:44:50 +0900 Subject: [PATCH 5/9] implement oidc discovery --- assets/js/oauth.js | 25 +++++++++++++++++++++++++ pages/index.vue | 3 +-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/assets/js/oauth.js b/assets/js/oauth.js index 5925b943c..583bae7d5 100644 --- a/assets/js/oauth.js +++ b/assets/js/oauth.js @@ -30,6 +30,23 @@ const parseQueryString = string => { segments.forEach(s => queryString[s[0]] = s[1]); return queryString; } +// Get OAuth configuration from OpenID Discovery endpoint +const getTokenConfiguration = async endpoint => { + const options = { + method: 'GET', + headers: { + 'Content-type': 'application/json' + } + } + try { + const response = await fetch(endpoint, options); + const config = await response.json(); + return config; + } catch (err) { + console.error('Request failed', err); + throw err; + } +} ////////////////////////////////////////////////////////////////////// // PKCE HELPER FUNCTIONS @@ -67,6 +84,7 @@ const pkceChallengeFromVerifier = async(v) => { // Initiate PKCE Auth Code flow when requested const tokenRequest = async({ + oidcDiscoveryUrl, grantType, authUrl, accessTokenUrl, @@ -74,6 +92,13 @@ const tokenRequest = async({ scope }) => { + // Check configuration URLs + if (oidcDiscoveryUrl !== '') { + const { authorization_endpoint, token_endpoint } = await getTokenConfiguration(oidcDiscoveryUrl); + authUrl = authorization_endpoint; + accessTokenUrl = token_endpoint; + } + // Store oauth information localStorage.setItem('token_endpoint', accessTokenUrl); localStorage.setItem('client_id', clientId); diff --git a/pages/index.vue b/pages/index.vue index 750212216..05b38a186 100644 --- a/pages/index.vue +++ b/pages/index.vue @@ -2455,8 +2455,7 @@ export default { authUrl: this.authUrl, accessTokenUrl: this.accessTokenUrl, clientId: this.clientId, - scope: this.scope, - clientAuth: this.clientAuth + scope: this.scope }; await tokenRequest(tokenReqParams); } catch (e) { From 2a818dc81d4cdf7833770a4de8c5f8b6641c2f29 Mon Sep 17 00:00:00 2001 From: Abdul Rifqi Al Abqary Date: Mon, 6 Jan 2020 16:50:20 +0900 Subject: [PATCH 6/9] comment correction --- assets/js/oauth.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/js/oauth.js b/assets/js/oauth.js index 583bae7d5..8bd7b0423 100644 --- a/assets/js/oauth.js +++ b/assets/js/oauth.js @@ -92,7 +92,7 @@ const tokenRequest = async({ scope }) => { - // Check configuration URLs + // Check oauth configuration if (oidcDiscoveryUrl !== '') { const { authorization_endpoint, token_endpoint } = await getTokenConfiguration(oidcDiscoveryUrl); authUrl = authorization_endpoint; From df48e85de52d94b1c4fa7914572c87e802d37afa Mon Sep 17 00:00:00 2001 From: Abdul Rifqi Al Abqary Date: Mon, 6 Jan 2020 18:17:50 +0900 Subject: [PATCH 7/9] added token managements --- pages/index.vue | 90 ++++++++++++++++++++++++++++++++++++++-------- store/mutations.js | 21 +++++++++-- store/state.js | 2 +- 3 files changed, 95 insertions(+), 18 deletions(-) diff --git a/pages/index.vue b/pages/index.vue index 05b38a186..8e9c5b041 100644 --- a/pages/index.vue +++ b/pages/index.vue @@ -994,13 +994,50 @@
  • -
    +
    +
      +
    • + +
    • +
    • + +
    • +
      +
    • + +
    • +
      +
    • - +
    @@ -1425,12 +1462,12 @@ export default { this.$store.commit("setState", { value, attribute: "bearerToken" }); } }, - token: { + tokens: { get() { - return this.$store.oauth2.token; + return this.$store.state.oauth2.tokens; }, set(value) { - this.$store.commit("setOauth2", { value, attribute: "token" }); + this.$store.commit("setOAuth2", { value, attribute: "tokens" }); } }, accessTokenName: { @@ -1438,7 +1475,7 @@ export default { return this.$store.state.oauth2.accessTokenName; }, set(value) { - this.$store.commit("setOauth2", { value, attribute: "accessTokenName" }); + this.$store.commit("setOAuth2", { value, attribute: "accessTokenName" }); } }, oidcDiscoveryUrl: { @@ -1446,7 +1483,7 @@ export default { return this.$store.state.oauth2.oidcDiscoveryUrl; }, set(value) { - this.$store.commit("setOauth2", { value, attribute: "oidcDiscoveryUrl" }); + this.$store.commit("setOAuth2", { value, attribute: "oidcDiscoveryUrl" }); } }, authUrl: { @@ -1454,7 +1491,7 @@ export default { return this.$store.state.oauth2.authUrl; }, set(value) { - this.$store.commit("setOauth2", { value, attribute: "authUrl" }); + this.$store.commit("setOAuth2", { value, attribute: "authUrl" }); } }, accessTokenUrl: { @@ -1462,7 +1499,7 @@ export default { return this.$store.state.oauth2.accessTokenUrl; }, set(value) { - this.$store.commit("setOauth2", { value, attribute: "accessTokenUrl" }); + this.$store.commit("setOAuth2", { value, attribute: "accessTokenUrl" }); } }, clientId: { @@ -1470,7 +1507,7 @@ export default { return this.$store.state.oauth2.clientId; }, set(value) { - this.$store.commit("setOauth2", { value, attribute: "clientId" }); + this.$store.commit("setOAuth2", { value, attribute: "clientId" }); } }, scope: { @@ -1478,7 +1515,7 @@ export default { return this.$store.state.oauth2.scope; }, set(value) { - this.$store.commit("setOauth2", { value, attribute: "scope" }); + this.$store.commit("setOAuth2", { value, attribute: "scope" }); } }, state: { @@ -1486,7 +1523,7 @@ export default { return this.$store.state.oauth2.state; }, set(value) { - this.$store.commit("setOauth2", { value, attribute: "state" }); + this.$store.commit("setOAuth2", { value, attribute: "state" }); } }, headers: { @@ -2470,6 +2507,29 @@ export default { this.bearerToken = tokenInfo.access_token; } }, + addOAuthToken() { + this.$store.commit("addOAuthToken", { + name: "", + value: "" + }); + return false; + }, + removeOAuthToken(index) { + // .slice() gives us an entirely new array rather than giving us just the reference + const oldTokens = this.tokens.slice(); + + this.$store.commit("removeOAuthToken", index); + this.$toast.error("Deleted", { + icon: "delete", + action: { + text: "Undo", + onClick: (e, toastObject) => { + this.tokens = oldTokens; + toastObject.remove(); + } + } + }); + }, saveToken(){ try { this.$toast.info("Access token saved"); diff --git a/store/mutations.js b/store/mutations.js index 67d17b696..bc76a5b93 100644 --- a/store/mutations.js +++ b/store/mutations.js @@ -87,7 +87,24 @@ export default { request.bodyParams[index].value = value; }, - setOauth2({ oauth2 }, { attribute, value }) { + setOAuth2({ oauth2 }, { attribute, value }) { oauth2[attribute] = value; - } + }, + + addOAuthToken({ oauth2 }, value) { + oauth2.tokens.push(value); + }, + + removeOAuthToken({ oauth2 }, index) { + oauth2.tokens.splice(index, 1); + }, + + setOAuthTokenName({ oauth2 }, { index, value }) { + oauth2.tokens[index].name = value; + }, + + setOAuthTokenValue({ oauth2 }, { index, value }) { + oauth2.tokens[index].value = value; + }, + }; diff --git a/store/state.js b/store/state.js index ed9467f1b..e5cb1c089 100644 --- a/store/state.js +++ b/store/state.js @@ -24,7 +24,7 @@ export default () => ({ query: "" }, oauth2: { - token: [], + tokens: [], accessTokenName: "", oidcDiscoveryUrl: "", authUrl: "", From f847cbe122aef342c7f22f8d7211ffb9f47b78c4 Mon Sep 17 00:00:00 2001 From: Abdul Rifqi Al Abqary Date: Tue, 7 Jan 2020 11:50:56 +0900 Subject: [PATCH 8/9] Added token management --- lang/en-US.js | 1 + pages/index.vue | 104 ++++++++++++++++++++++++--------------------- store/mutations.js | 6 +-- 3 files changed, 57 insertions(+), 54 deletions(-) diff --git a/lang/en-US.js b/lang/en-US.js index 280da3e56..2725ec4cc 100644 --- a/lang/en-US.js +++ b/lang/en-US.js @@ -88,6 +88,7 @@ export default { start: "Start", stop: "Stop", access_token: "Access Token", + token_list: "Token List", get_token: "Get New Token", manage_token: "Manage Access Token", save_token: "Save Access Token", diff --git a/pages/index.vue b/pages/index.vue index 8e9c5b041..9d3a5294a 100644 --- a/pages/index.vue +++ b/pages/index.vue @@ -400,7 +400,7 @@ @click="showTokenList = !showTokenList" v-tooltip.bottom="$t('use_token')" > - input + open_in_new
    -
      +
        +
      • +
        + +
        + +
        +
        +
      • +
      +
      • - +
      • -
        +
        +
      • + +
      -
        -
      • - -
      • -
      -
    -
    -
    - - - - - -
    +
    @@ -2364,6 +2360,7 @@ export default { this.httpPassword = ""; this.bearerToken = ""; this.showTokenRequest = false; + this.tokens = []; break; case "headers": this.headers = []; @@ -2379,6 +2376,9 @@ export default { this.clientId = ""; this.scope = ""; break; + case "tokens": + this.tokens = []; + break; default: (this.label = ""), (this.method = "GET"), @@ -2393,6 +2393,14 @@ export default { this.params = []; this.bodyParams = []; this.rawParams = ""; + this.showTokenRequest = false; + this.tokens = []; + this.accessTokenName = ""; + this.oidcDiscoveryUrl = ""; + this.authUrl = ""; + this.accessTokenUrl = ""; + this.clientId = ""; + this.scope = ""; } e.target.innerHTML = this.doneButton; this.$toast.info("Cleared", { @@ -2478,7 +2486,7 @@ export default { }); } }, - async handleAccessTokenRequest(){ + async handleAccessTokenRequest() { if (this.oidcDiscoveryUrl === "" && (this.authUrl === "" || this.accessTokenUrl === "")) { this.$toast.error("Please complete configuration urls.", { icon: "error" @@ -2505,12 +2513,16 @@ export default { let tokenInfo = await oauthRedirect(); if(tokenInfo.hasOwnProperty('access_token')) { this.bearerToken = tokenInfo.access_token; + this.addOAuthToken({ + name: this.accessTokenName, + value: tokenInfo.access_token + }); } }, - addOAuthToken() { + addOAuthToken({name, value}) { this.$store.commit("addOAuthToken", { - name: "", - value: "" + name, + value }); return false; }, @@ -2530,17 +2542,11 @@ export default { } }); }, - saveToken(){ - try { - this.$toast.info("Access token saved"); - this.showTokenList = false; - } catch (e) { - this.$toast.error(e, { - icon: "code" - }); - } + useOAuthToken(value) { + this.bearerToken = value; + this.showTokenList = false; }, - saveTokenRequest(){ + saveTokenRequest() { try { this.$toast.info("Token request saved"); this.showTokenRequestList = false; diff --git a/store/mutations.js b/store/mutations.js index bc76a5b93..446fbef65 100644 --- a/store/mutations.js +++ b/store/mutations.js @@ -101,10 +101,6 @@ export default { setOAuthTokenName({ oauth2 }, { index, value }) { oauth2.tokens[index].name = value; - }, - - setOAuthTokenValue({ oauth2 }, { index, value }) { - oauth2.tokens[index].value = value; - }, + } }; From 161c0b7b88da2347fea0f5a70db4977b8ecc34e9 Mon Sep 17 00:00:00 2001 From: Abdul Rifqi Al Abqary Date: Tue, 7 Jan 2020 18:46:16 +0900 Subject: [PATCH 9/9] added token request management --- lang/en-US.js | 7 +- pages/index.vue | 209 +++++++++++++++++++++++++++++++++++---------- store/mutations.js | 9 +- store/state.js | 3 + 4 files changed, 181 insertions(+), 47 deletions(-) diff --git a/lang/en-US.js b/lang/en-US.js index 2725ec4cc..9ad7e21a1 100644 --- a/lang/en-US.js +++ b/lang/en-US.js @@ -94,7 +94,11 @@ export default { save_token: "Save Access Token", use_token: "Use Saved Token", request_token: "Request Token", - save_token_request: "Save Token Request", + save_token_req: "Save Token Request", + manage_token_req: "Manage Token Request", + use_token_req: "Use Token Request", + token_req_name: "Request Name", + token_req_details: "Request Details", token_name: "Token Name", oidc_discovery_url: "OIDC Discovery URL", auth_url: "Auth URL", @@ -102,4 +106,5 @@ export default { client_id: "Client ID", scope: "Scope", state: "State", + token_req_list: "Token Request List" }; diff --git a/pages/index.vue b/pages/index.vue index 9d3a5294a..bcfa5f980 100644 --- a/pages/index.vue +++ b/pages/index.vue @@ -436,7 +436,7 @@ @@ -939,46 +939,6 @@
    - -
    -
      -
    • -
      -

      {{ $t("save_token_request") }}

      -
      - -
      -
      -
    • -
    -
    -
    -
      -
    • - -
    • -
    -
    -
    -
    - - - - - -
    -
    -
    -
      @@ -994,7 +954,7 @@
    -
    +
    • @@ -1051,6 +1011,91 @@
      + + +
      +
        +
      • +
        +

        {{ $t("manage_token_req") }}

        +
        + +
        +
        +
      • +
      +
      +
      +
        +
      • +
        + +
        + + +
        +
        + + + +
      • +
      +
        +
      • + + +
      • +
      +
        +
      • + + +
      • +
      +
      +
      +
      + + + + +
      +
      +
      +
    @@ -1466,6 +1511,30 @@ export default { this.$store.commit("setOAuth2", { value, attribute: "tokens" }); } }, + tokenReqs: { + get() { + return this.$store.state.oauth2.tokenReqs; + }, + set(value) { + this.$store.commit("setOAuth2", { value, attribute: "tokenReqs" }); + } + }, + tokenReqSelect: { + get() { + return this.$store.state.oauth2.tokenReqSelect; + }, + set(value) { + this.$store.commit("setOAuth2", { value, attribute: "tokenReqSelect" }); + } + }, + tokenReqName: { + get() { + return this.$store.state.oauth2.tokenReqName; + }, + set(value) { + this.$store.commit("setOAuth2", { value, attribute: "tokenReqName" }); + } + }, accessTokenName: { get() { return this.$store.state.oauth2.accessTokenName; @@ -1812,6 +1881,16 @@ export default { } return requestString.join("").slice(0, -3); } + }, + tokenReqDetails() { + const details = { + oidcDiscoveryUrl: this.oidcDiscoveryUrl, + authUrl: this.authUrl, + accessTokenUrl: this.accessTokenUrl, + clientId: this.clientId, + scope: this.scope + }; + return JSON.stringify(details, null, 2); } }, methods: { @@ -2361,6 +2440,7 @@ export default { this.bearerToken = ""; this.showTokenRequest = false; this.tokens = []; + this.tokenReqs = []; break; case "headers": this.headers = []; @@ -2379,6 +2459,8 @@ export default { case "tokens": this.tokens = []; break; + case "tokenReqs": + this.tokenReqs = []; default: (this.label = ""), (this.method = "GET"), @@ -2395,6 +2477,7 @@ export default { this.rawParams = ""; this.showTokenRequest = false; this.tokens = []; + this.tokenReqs = []; this.accessTokenName = ""; this.oidcDiscoveryUrl = ""; this.authUrl = ""; @@ -2527,9 +2610,7 @@ export default { return false; }, removeOAuthToken(index) { - // .slice() gives us an entirely new array rather than giving us just the reference const oldTokens = this.tokens.slice(); - this.$store.commit("removeOAuthToken", index); this.$toast.error("Deleted", { icon: "delete", @@ -2546,8 +2627,14 @@ export default { this.bearerToken = value; this.showTokenList = false; }, - saveTokenRequest() { + addOAuthTokenReq() { try { + const name = this.tokenReqName; + const details = JSON.parse(this.tokenReqDetails); + this.$store.commit("addOAuthTokenReq", { + name, + details + }); this.$toast.info("Token request saved"); this.showTokenRequestList = false; } catch (e) { @@ -2555,6 +2642,38 @@ export default { icon: "code" }); } + }, + removeOAuthTokenReq(index) { + const oldTokenReqs = this.tokenReqs.slice(); + let targetReqIndex = this.tokenReqs.findIndex(tokenReq => tokenReq.name === this.tokenReqName); + if (targetReqIndex < 0) return; + this.$store.commit("removeOAuthTokenReq", targetReqIndex); + this.$toast.error("Deleted", { + icon: "delete", + action: { + text: "Undo", + onClick: (e, toastObject) => { + this.tokenReqs = oldTokenReqs; + toastObject.remove(); + } + } + }); + }, + tokenReqChange(event) { + let targetReq = this.tokenReqs.find(tokenReq => tokenReq.name === event.target.value); + let { + oidcDiscoveryUrl, + authUrl, + accessTokenUrl, + clientId, + scope + } = targetReq.details; + this.tokenReqName = targetReq.name; + this.oidcDiscoveryUrl = oidcDiscoveryUrl; + this.authUrl = authUrl; + this.accessTokenUrl = accessTokenUrl; + this.clientId = clientId; + this.scope = scope; } }, async mounted() { diff --git a/store/mutations.js b/store/mutations.js index 446fbef65..64d178f57 100644 --- a/store/mutations.js +++ b/store/mutations.js @@ -101,6 +101,13 @@ export default { setOAuthTokenName({ oauth2 }, { index, value }) { oauth2.tokens[index].name = value; - } + }, + addOAuthTokenReq({ oauth2 }, value) { + oauth2.tokenReqs.push(value); + }, + + removeOAuthTokenReq({ oauth2 }, index) { + oauth2.tokenReqs.splice(index, 1); + } }; diff --git a/store/state.js b/store/state.js index e5cb1c089..e6ab3b51d 100644 --- a/store/state.js +++ b/store/state.js @@ -25,6 +25,9 @@ export default () => ({ }, oauth2: { tokens: [], + tokenReqs: [], + tokenReqSelect: "", + tokenReqName: "", accessTokenName: "", oidcDiscoveryUrl: "", authUrl: "",