├── assets
├── .gitkeep
├── gifs
│ └── .gitkeep
└── screenshots
│ └── .gitkeep
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── feature_request.md
│ └── bug_report.md
└── workflows
│ └── artifacts-zip.yml
├── devtools.html
├── icons
├── icon16.png
├── icon32.png
├── icon48.png
└── icon128.png
├── part.edge.json
├── devtools.js
├── .gitignore
├── styles
├── popup.scss
├── options.scss
└── jwt-panel.scss
├── .gitattributes
├── part.firefox.json
├── popup.html
├── scripts
├── generateChromeExtension.sh
├── generateEdgeExtension.sh
└── generateFirefoxExtension.sh
├── jwt-panel.html
├── EDGE.md
├── CHROME.md
├── FIREFOX.md
├── README.md
├── manifest.json
├── options.html
├── LICENSE
├── options.js
├── package.json
├── jwt-panel.js
└── encoder.js
/assets/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/gifs/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/screenshots/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: [piraces]
2 | ko_fi: piraces
3 | liberapay: piraces
4 |
--------------------------------------------------------------------------------
/devtools.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/icons/icon16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piraces/jwt-inspector-malicious-poc/main/icons/icon16.png
--------------------------------------------------------------------------------
/icons/icon32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piraces/jwt-inspector-malicious-poc/main/icons/icon32.png
--------------------------------------------------------------------------------
/icons/icon48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piraces/jwt-inspector-malicious-poc/main/icons/icon48.png
--------------------------------------------------------------------------------
/part.edge.json:
--------------------------------------------------------------------------------
1 | {
2 | "update_URL": "https://edge.microsoft.com/extensionwebstorebase/v1/crx"
3 | }
--------------------------------------------------------------------------------
/icons/icon128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/piraces/jwt-inspector-malicious-poc/main/icons/icon128.png
--------------------------------------------------------------------------------
/devtools.js:
--------------------------------------------------------------------------------
1 | chrome.devtools.panels.create(
2 | "JWT", "", "jwt-panel.html", function(panel) {}
3 | );
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .sass-cache
2 | styles/*.css
3 | styles/*.min.css
4 | styles/*.min.css.map
5 | styles/*.css.map
6 | node_modules
7 | .DS_Store
8 | *.zip
9 | .vs
--------------------------------------------------------------------------------
/styles/popup.scss:
--------------------------------------------------------------------------------
1 | * {
2 | font-family: monospace;
3 | }
4 |
5 | body {
6 | width: 20rem;
7 | }
8 |
9 | p {
10 | font-size: medium;
11 | }
12 |
13 | a {
14 | font-size: medium;
15 | }
16 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Set the default behavior, in case people don't have core.autocrlf set.
2 | * text=lf
3 |
4 | # Denote all files that are truly binary and should not be modified.
5 | *.png binary
6 | *.jpg binary
7 | *.gif binary
8 |
--------------------------------------------------------------------------------
/part.firefox.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifest_version": 2,
3 | "browser_specific_settings": {
4 | "gecko": {
5 | "id": "raul.piraces@gmail.com",
6 | "strict_min_version": "57.0"
7 | }
8 | },
9 | "browser_action": {
10 | "default_popup": "popup.html"
11 | }
12 | }
--------------------------------------------------------------------------------
/styles/options.scss:
--------------------------------------------------------------------------------
1 | #status {
2 | height: 1.5em;
3 | }
4 |
5 | @media (prefers-color-scheme: dark) {
6 | * {
7 | color: silver;
8 | background-color: #202124;
9 | }
10 |
11 | button {
12 | background-color: silver;
13 | color: #202124;
14 | }
15 |
16 | input {
17 | border-style: solid;
18 | border-color: #333333;
19 | }
20 | }
--------------------------------------------------------------------------------
/popup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Open developer tools and select the JWT tab
9 | Use a site which sends JWT bearer tokens in the Authorization HTTP header
10 | See the token contents in the developer tools panel
11 |
12 | More information
13 |
14 |
--------------------------------------------------------------------------------
/scripts/generateChromeExtension.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | npm install
4 | npm run process-styles
5 | npm run js-minify
6 | npm run clean
7 | npm install --production
8 | rm -rf assets && rm -rf .github && rm -rf .git && rm .gitignore package-lock.json package.json part.edge.json part.firefox.json styles/options.css styles/options.css.map styles/options.scss styles/popup.css styles/popup.css.map styles/popup.scss styles/jwt-panel.css styles/jwt-panel.css.map styles/jwt-panel.scss
9 | zip -r jwt-dev-inspector-chrome.zip .
--------------------------------------------------------------------------------
/jwt-panel.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Copy token to clipboard
11 |
12 |
13 | Header
14 |
15 |
16 |
17 | Claims
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/scripts/generateEdgeExtension.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | npm install
4 | npm run process-styles
5 | npm run js-minify
6 | npm run clean
7 | npm install --production
8 | echo $(jq -s 'reduce .[] as $item ({}; . * $item)' manifest.json part.edge.json) > manifest.json
9 | rm -rf assets && rm -rf .github && rm -rf .git && rm .gitignore package-lock.json package.json part.edge.json part.firefox.json CHANGELOG.md styles/options.css styles/options.css.map styles/options.scss styles/popup.css styles/popup.css.map styles/popup.scss styles/jwt-panel.css styles/jwt-panel.css.map styles/jwt-panel.scss
10 | zip -r jwt-dev-inspector-edge.zip .
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: "[FEAT] - "
5 | labels: enhancement
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/scripts/generateFirefoxExtension.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | npm install
4 | npm run process-styles
5 | npm run js-minify
6 | npm run clean
7 | npm install --production
8 | echo $(jq 'del(.background)' manifest.json) > manifest.json
9 | echo $(jq 'del(.action)' manifest.json) > manifest.json
10 | echo $(jq -s 'reduce .[] as $item ({}; . * $item)' manifest.json part.firefox.json) > manifest.json
11 | rm -rf assets && rm -rf .github && rm -rf .git && rm .gitignore package-lock.json package.json part.edge.json part.firefox.json styles/options.css styles/options.css.map styles/options.scss styles/popup.css styles/popup.css.map styles/popup.scss styles/jwt-panel.css styles/jwt-panel.css.map styles/jwt-panel.scss
12 | zip -r jwt-dev-inspector-firefox.xpi .
--------------------------------------------------------------------------------
/EDGE.md:
--------------------------------------------------------------------------------
1 | # Generating the ZIP file for the Edge extension
2 |
3 | This extension has a script in the `./scripts` folder named `generateEdgeExtension.sh` which is executed in the publish pipeline in order to generate the final ZIP to publish to the Edge extensions store.
4 |
5 | This script executes different `npm` script in order to build the extension for production mode, delete all unneeded files and generate the final `manifest.json`.
6 |
7 |
8 | The following tools are used with the following purposes:
9 | - `npm`: generate the final extension files and clean unneeded files.
10 | - `jq`: replacements inside the `manifest.json` file to make it cross-browser supported.
11 | - `find`: removal of unneeded files.
12 | - `zip`: generate the final bundled extension in `.zip` format.
--------------------------------------------------------------------------------
/CHROME.md:
--------------------------------------------------------------------------------
1 | # Generating the ZIP file for the Chrome extension
2 |
3 | This extension has a script in the `./scripts` folder named `generateChromeExtension.sh` which is executed in the publish pipeline in order to generate the final ZIP to publish to the Chrome extensions store.
4 |
5 | This script executes different `npm` script in order to build the extension for production mode, delete all unneeded files and generate the final `manifest.json`.
6 |
7 |
8 | The following tools are used with the following purposes:
9 | - `npm`: generate the final extension files and clean unneeded files.
10 | - `jq`: replacements inside the `manifest.json` file to make it cross-browser supported.
11 | - `find`: removal of unneeded files.
12 | - `zip`: generate the final bundled extension in `.zip` format.
--------------------------------------------------------------------------------
/FIREFOX.md:
--------------------------------------------------------------------------------
1 | # Generating the XPI file for the Firefox extension
2 |
3 | This extension has a script in the `./scripts` folder named `generateFirefoxExtension.sh` which is executed in the publish pipeline in order to generate the final XPI to publish to the Firefox Add-ons store.
4 |
5 | This script executes different `npm` script in order to build the extension for production mode, delete all unneeded files and generate the final `manifest.json`.
6 |
7 |
8 | The following tools are used with the following purposes:
9 | - `npm`: generate the final extension files and clean unneeded files.
10 | - `jq`: replacements inside the `manifest.json` file to make it cross-browser supported.
11 | - `find`: removal of unneeded files.
12 | - `zip`: generate the final bundled extension in `.xpi` format.
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: "[BUG] - "
5 | labels: bug
6 | assignees: piraces
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Additional context**
32 | Add any other context about the problem here.
33 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # JWT Inspector
2 |
3 | JWT Inspector is a Chrome extension which makes it easy to inspect the content of
4 | any JWT bearer token sent by a webapp.
5 |
6 | The extension adds a new **JWT** tab in Chrome's Developer Tools.
7 | When the tab is open, the extension inspects all server requests and picks out
8 | the token from any request which has an `Authorization` header containing a JWT
9 | bearer token.
10 |
11 | # Development
12 |
13 | The main implementation file is `jwt-panel.js`.
14 |
15 | You can install the extension as "unpacked" straight from the source directory to
16 | directly test any changes you make. (Enable "Developer mode" in
17 | [chrome://extensions/](chrome://extensions/)
18 | to make this option available.)
19 |
20 | Running `build.sh` packages the extension for upload to the chrome web store.
21 | Don't forget to change the version number in `manifest.json` before creating the package.
22 |
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "JWT Dev Inspector",
3 | "version": "1.6.2",
4 | "description": "Display JWT bearer tokens in a new tab in Chrome's Developer Tools",
5 | "author": "Raúl Piracés",
6 | "homepage_url": "https://piraces.github.io/jwt-inspector/",
7 | "manifest_version": 3,
8 | "devtools_page": "devtools.html",
9 | "options_ui": {
10 | "page": "options.html",
11 | "open_in_tab": false
12 | },
13 | "permissions": ["storage"],
14 | "incognito": "split",
15 | "action": {
16 | "default_icon": {
17 | "16": "/icons/icon16.png",
18 | "32": "/icons/icon32.png",
19 | "48": "/icons/icon48.png",
20 | "128": "/icons/icon128.png"
21 | },
22 | "default_popup": "popup.html"
23 | },
24 | "icons": {
25 | "16": "/icons/icon16.png",
26 | "32": "/icons/icon32.png",
27 | "48": "/icons/icon48.png",
28 | "128": "/icons/icon128.png"
29 | }
30 | }
--------------------------------------------------------------------------------
/options.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | JWT Inspector Options
6 |
7 |
8 |
41 |
42 |
43 | Save
44 | Reset
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Tore Green
4 |
5 | Modifications to the theme and codebase Copyright (c) 2023 Raúl Piracés @piraces
6 |
7 | Permission is hereby granted, free of charge, to any person obtaining a copy
8 | of this software and associated documentation files (the "Software"), to deal
9 | in the Software without restriction, including without limitation the rights
10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | copies of the Software, and to permit persons to whom the Software is
12 | furnished to do so, subject to the following conditions:
13 |
14 | The above copyright notice and this permission notice shall be included in all
15 | copies or substantial portions of the Software.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 | SOFTWARE.
24 |
--------------------------------------------------------------------------------
/styles/jwt-panel.scss:
--------------------------------------------------------------------------------
1 | td:first-child {
2 | color: green;
3 | font-weight: bold;
4 | text-align: right;
5 | vertical-align: text-top;
6 | padding-right: 0.5em;
7 | min-width: 70px;
8 | max-width: 50%;
9 | overflow-wrap: anywhere;
10 | }
11 |
12 | td:first-child::after {
13 | content: ":";
14 | }
15 |
16 | .ts {
17 | font-style: italic;
18 | color: firebrick;
19 | }
20 |
21 | .ts::before {
22 | content: " (";
23 | }
24 |
25 | .ts::after {
26 | content: ")";
27 | }
28 |
29 | fieldset {
30 | border: 2px solid #000;
31 | -moz-border-radius: 8px;
32 | -webkit-border-radius: 8px;
33 | border-radius: 8px;
34 | }
35 |
36 | legend {
37 | color: #000;
38 | font-size: 1.5em;
39 | }
40 |
41 | @media (prefers-color-scheme: dark) {
42 | * {
43 | color: silver;
44 | background-color: #202124;
45 | }
46 |
47 | button {
48 | background-color: silver;
49 | color: #202124;
50 | }
51 |
52 | td:first-child {
53 | color: darkseagreen;
54 | }
55 |
56 | .ts {
57 | color: lightcoral;
58 | }
59 |
60 | fieldset {
61 | border: thick solid #494c50;
62 | }
63 |
64 | legend {
65 | color: #9aa0a5;
66 | }
67 | }
--------------------------------------------------------------------------------
/options.js:
--------------------------------------------------------------------------------
1 | // Saves options to chrome.storage
2 | function save_options() {
3 | var header_name = document.getElementById('header_name').value;
4 | var header_prefix = document.getElementById('header_prefix').value;
5 | var copy_prefix = document.getElementById('copy_prefix').checked;
6 | chrome.storage.local.set({
7 | header_name: header_name,
8 | header_prefix: header_prefix,
9 | copy_prefix: copy_prefix
10 | }, function() {
11 | // Update status to let user know options were saved.
12 | var status = document.getElementById('status');
13 | status.textContent = 'Options saved.';
14 | setTimeout(function() {
15 | status.textContent = '';
16 | }, 750);
17 | });
18 | }
19 |
20 | function restore_options() {
21 | chrome.storage.local.get({
22 | header_name: "Authorization",
23 | header_prefix: "Bearer",
24 | copy_prefix: false
25 | }, function(items) {
26 | document.getElementById('header_name').value = items.header_name;
27 | document.getElementById('header_prefix').value = items.header_prefix;
28 | document.getElementById('copy_prefix').checked = items.copy_prefix;
29 | });
30 | }
31 |
32 | // Resets options to default values
33 | function reset_options() {
34 | document.getElementById('header_name').value = "Authorization";
35 | document.getElementById('header_prefix').value = "Bearer";
36 | document.getElementById('copy_prefix').checked = false;
37 | save_options();
38 | }
39 |
40 | document.addEventListener('DOMContentLoaded', restore_options);
41 | document.getElementById('save').addEventListener('click', save_options);
42 | document.getElementById('reset').addEventListener('click', reset_options);
43 |
--------------------------------------------------------------------------------
/.github/workflows/artifacts-zip.yml:
--------------------------------------------------------------------------------
1 | name: Generate artifacts to publish
2 |
3 | on:
4 | release:
5 | types: [published]
6 | workflow_dispatch:
7 |
8 | jobs:
9 | chrome:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v3
13 | - name: Use Node.js 18.x
14 | uses: actions/setup-node@v3
15 | with:
16 | node-version: 18
17 | - run: |
18 | chmod +x ./scripts/generateChromeExtension.sh
19 | ./scripts/generateChromeExtension.sh
20 | - uses: actions/upload-artifact@v3
21 | with:
22 | name: jwt-dev-inspector-chrome
23 | path: jwt-dev-inspector-chrome.zip
24 |
25 | firefox:
26 | runs-on: ubuntu-latest
27 | steps:
28 | - uses: actions/checkout@v3
29 | - name: Use Node.js 18.x
30 | uses: actions/setup-node@v3
31 | with:
32 | node-version: 18
33 | - run: |
34 | chmod +x ./scripts/generateFirefoxExtension.sh
35 | ./scripts/generateFirefoxExtension.sh
36 | - uses: actions/upload-artifact@v3
37 | with:
38 | name: jwt-dev-inspector-firefox
39 | path: jwt-dev-inspector-firefox.xpi
40 |
41 | edge:
42 | runs-on: ubuntu-latest
43 | steps:
44 | - uses: actions/checkout@v3
45 | - name: Use Node.js 18.x
46 | uses: actions/setup-node@v3
47 | with:
48 | node-version: 18
49 | - run: |
50 | chmod +x ./scripts/generateEdgeExtension.sh
51 | ./scripts/generateEdgeExtension.sh
52 | - uses: actions/upload-artifact@v3
53 | with:
54 | name: jwt-dev-inspector-edge
55 | path: jwt-dev-inspector-edge.zip
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jwt-dev-inspector",
3 | "version": "1.6.2",
4 | "description": "Display JWT bearer tokens in a new tab in Chrome's Developer Tools",
5 | "main": "main.js",
6 | "scripts": {
7 | "process-styles": "npm run compile-styles && npm run css-minify",
8 | "compile-styles": "node-sass --output-style expanded --source-map true --source-map-contents true --precision 6 styles/popup.scss styles/popup.css && node-sass --output-style expanded --source-map true --source-map-contents true --precision 6 styles/options.scss styles/options.css && node-sass --output-style expanded --source-map true --source-map-contents true --precision 6 styles/jwt-panel.scss styles/jwt-panel.css",
9 | "css-minify": "cleancss --format breaksWith=lf --output styles/popup.min.css styles/popup.css && cleancss --format breaksWith=lf --output styles/options.min.css styles/options.css && cleancss --format breaksWith=lf --output styles/jwt-panel.min.css styles/jwt-panel.css",
10 | "js-minify": "uglifyjs devtools.js -o devtools.js && uglifyjs encoder.js -o encoder.js && uglifyjs jwt-panel.js -o jwt-panel.js && uglifyjs options.js -o options.js",
11 | "clean": "rimraf node_modules"
12 | },
13 | "repository": {
14 | "type": "git",
15 | "url": "git+https://github.com/piraces/jwt-inspector.git"
16 | },
17 | "keywords": [
18 | "chrome-extension",
19 | "extension",
20 | "JWT",
21 | "authentication"
22 | ],
23 | "author": "Raúl Piracés",
24 | "license": "MIT",
25 | "bugs": {
26 | "url": "https://github.com/piraces/jwt-inspector/issues"
27 | },
28 | "homepage": "https://github.com/piraces/jwt-inspector#readme",
29 | "dependencies": {
30 | },
31 | "devDependencies": {
32 | "clean-css-cli": "^5.6.2",
33 | "node-sass": "^8.0.0",
34 | "rimraf": "^4.1.2",
35 | "uglify-js": "^3.17.4"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/jwt-panel.js:
--------------------------------------------------------------------------------
1 | var options = {
2 | header_name: "authorization",
3 | header_prefix: ["Bearer "],
4 | copy_prefix: false
5 | };
6 |
7 | function setOptions(o) {
8 | options.header_name = o.header_name.toLowerCase().trim();
9 | options.header_prefix = typeof o.header_prefix == "string" ? o.header_prefix.split(',') : o.header_prefix;
10 | options.copy_prefix = o.copy_prefix;
11 | for(var i = 0 ; i0 && !options.header_prefix[i].endsWith(' ')) {
13 | options.header_prefix[i] += ' ';
14 | }
15 | }
16 | var caption = document.getElementById("caption");
17 | var p = options.header_prefix.length>1 ? '{'+options.header_prefix.join()+'}' : options.header_prefix[0];
18 | caption.innerHTML = 'Waiting for request with '+Encoder.htmlEncode(o.header_name)+
19 | ': '+Encoder.htmlEncode(p)+' [token] '+
20 | ' (Go to Extentions > JWT Inspector > Options to customize)';
21 | }
22 |
23 | function bearer_token(h) {
24 | if(h && h.name && h.name.toLowerCase() == options.header_name && h.value) {
25 | var p = options.header_prefix.find( s => h.value.startsWith(s) );
26 | if(p) {
27 | return { prefix:p , tok:h.value.substring(p.length) };
28 | }
29 | }
30 | return null;
31 | }
32 |
33 | function isObject(obj) {
34 | var type = typeof obj;
35 | return type === 'function' || type === 'object' && !!obj;
36 | }
37 |
38 | const ts_claims = ["exp","iat","nbf"];
39 |
40 | function renderClaims(claims) {
41 | var table = document.createElement("table");
42 | for(var c in claims) {
43 | var row = document.createElement("tr");
44 | var td1 = document.createElement("td");
45 | td1.appendChild(document.createTextNode(Encoder.htmlEncode(String(c))));
46 | row.appendChild(td1);
47 | var td2 = document.createElement("td");
48 | if(isObject(claims[c])) {
49 | td2.appendChild(renderClaims(claims[c]));
50 | } else {
51 | td2.appendChild(document.createTextNode(Encoder.htmlEncode(String(claims[c]))));
52 | if(ts_claims.includes(c)) {
53 | var ts = document.createElement("span");
54 | ts.className = "ts";
55 | var d = new Date(claims[c]*1000);
56 | ts.appendChild(document.createTextNode(d.toLocaleString()));
57 | td2.appendChild(ts);
58 | }
59 | }
60 | row.appendChild(td2);
61 | table.appendChild(row);
62 | }
63 | return table;
64 | }
65 |
66 | function render(header, claims, url, time) {
67 |
68 | var divHeader = document.getElementById("header");
69 | var dlHeader = renderClaims(header);
70 | divHeader.innerHTML = "";
71 | divHeader.appendChild(dlHeader);
72 |
73 | var div = document.getElementById("claims");
74 | var dl = renderClaims(claims);
75 | div.innerHTML = "";
76 | div.appendChild(dl);
77 |
78 | var caption = document.getElementById("caption");
79 | caption.innerHTML = "Bearer token extracted from request to "+Encoder.htmlEncode(String(url));
80 | var ts = document.createElement("span");
81 | ts.className = "ts";
82 | ts.appendChild(document.createTextNode(Encoder.htmlEncode(String(time))));
83 | caption.appendChild(ts);
84 | }
85 |
86 | function updateCopyButton(p,tok) {
87 | var b = document.getElementById("copy_token");
88 | b.dataset.token = options.copy_prefix ? p+tok : tok;
89 | b.disabled = false;
90 | }
91 |
92 | // Taken from: https://stackoverflow.com/a/18455088/1823175
93 | function copyTextToClipboard(text) {
94 | //Create a textbox field where we can insert text to.
95 | var copyFrom = document.createElement("textarea");
96 |
97 | //Set the text content to be the text you wished to copy.
98 | copyFrom.textContent = text;
99 |
100 | //Append the textbox field into the body as a child.
101 | //"execCommand()" only works when there exists selected text, and the text is inside
102 | //document.body (meaning the text is part of a valid rendered HTML element).
103 | document.body.appendChild(copyFrom);
104 |
105 | //Select all the text!
106 | copyFrom.select();
107 |
108 | //Execute command
109 | document.execCommand('copy');
110 |
111 | //(Optional) De-select the text using blur().
112 | copyFrom.blur();
113 |
114 | //Remove the textbox field from the document.body, so no other JavaScript nor
115 | //other elements can get access to this.
116 | document.body.removeChild(copyFrom);
117 | }
118 |
119 | function copyToken() {
120 | var t = this.dataset.token;
121 | copyTextToClipboard(t);
122 | }
123 |
124 | function onRequestFinished(request) {
125 | var h = bearer_token(request.request.headers.find(bearer_token));
126 | if(!h) return;
127 | try {
128 | var parts = h.tok.split('.');
129 | var header = JSON.parse(atob(parts[0]));
130 | var claims = JSON.parse(atob(parts[1]));
131 | render(header, claims, request.request.url, request.startedDateTime);
132 | updateCopyButton(h.prefix,h.tok);
133 | } catch (error) {
134 | // Not a token we can extract and decode
135 | }
136 | }
137 |
138 | chrome.devtools.network.onRequestFinished.addListener(onRequestFinished);
139 | window.onload = function() {
140 | document.getElementById("copy_token").onclick = copyToken;
141 | chrome.storage.local.get(options, setOptions);
142 | };
143 | chrome.storage.onChanged.addListener( function(changes, namespace) {
144 | chrome.storage.local.get(options, setOptions);
145 | });
146 |
--------------------------------------------------------------------------------
/encoder.js:
--------------------------------------------------------------------------------
1 | /**
2 | * A Javascript object to encode and/or decode html characters using HTML or Numeric entities that handles double or partial encoding
3 | * Author: R Reid
4 | * source: http://www.strictly-software.com/htmlencode
5 | * Licences: GPL, The MIT License (MIT)
6 | * Copyright: (c) 2011 Robert Reid - Strictly-Software.com
7 | *
8 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
9 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
10 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
11 | *
12 | * Revision:
13 | * 2011-07-14, Jacques-Yves Bleau:
14 | * - fixed conversion error with capitalized accentuated characters
15 | * + converted arr1 and arr2 to object property to remove redundancy
16 | *
17 | * Revision:
18 | * 2011-11-10, Ce-Yi Hio:
19 | * - fixed conversion error with a number of capitalized entity characters
20 | *
21 | * Revision:
22 | * 2011-11-10, Rob Reid:
23 | * - changed array format
24 | *
25 | * Revision:
26 | * 2012-09-23, Alex Oss:
27 | * - replaced string concatonation in numEncode with string builder, push and join for peformance with ammendments by Rob Reid
28 | */
29 |
30 | Encoder = {
31 |
32 | // When encoding do we convert characters into html or numerical entities
33 | EncodeType : "entity", // entity OR numerical
34 |
35 | isEmpty : function(val){
36 | if(val){
37 | return ((val===null) || val.length==0 || /^\s+$/.test(val));
38 | }else{
39 | return true;
40 | }
41 | },
42 |
43 | // arrays for conversion from HTML Entities to Numerical values
44 | arr1: [' ','¡','¢','£','¤','¥','¦','§','¨','©','ª','«','¬','','®','¯','°','±','²','³','´','µ','¶','·','¸','¹','º','»','¼','½','¾','¿','À','Á','Â','Ã','Ä','Å','Æ','Ç','È','É','Ê','Ë','Ì','Í','Î','Ï','Ð','Ñ','Ò','Ó','Ô','Õ','Ö','×','Ø','Ù','Ú','Û','Ü','Ý','Þ','ß','à','á','â','ã','ä','å','æ','ç','è','é','ê','ë','ì','í','î','ï','ð','ñ','ò','ó','ô','õ','ö','÷','ø','ù','ú','û','ü','ý','þ','ÿ','"','&','<','>','Œ','œ','Š','š','Ÿ','ˆ','˜',' ',' ',' ','','','','','–','—','‘','’','‚','“','”','„','†','‡','‰','‹','›','€','ƒ','Α','Β','Γ','Δ','Ε','Ζ','Η','Θ','Ι','Κ','Λ','Μ','Ν','Ξ','Ο','Π','Ρ','Σ','Τ','Υ','Φ','Χ','Ψ','Ω','α','β','γ','δ','ε','ζ','η','θ','ι','κ','λ','μ','ν','ξ','ο','π','ρ','ς','σ','τ','υ','φ','χ','ψ','ω','ϑ','ϒ','ϖ','•','…','′','″','‾','⁄','℘','ℑ','ℜ','™','ℵ','←','↑','→','↓','↔','↵','⇐','⇑','⇒','⇓','⇔','∀','∂','∃','∅','∇','∈','∉','∋','∏','∑','−','∗','√','∝','∞','∠','∧','∨','∩','∪','∫','∴','∼','≅','≈','≠','≡','≤','≥','⊂','⊃','⊄','⊆','⊇','⊕','⊗','⊥','⋅','⌈','⌉','⌊','⌋','〈','〉','◊','♠','♣','♥','♦'],
45 | arr2: [' ','¡','¢','£','¤','¥','¦','§','¨','©','ª','«','¬','','®','¯','°','±','²','³','´','µ','¶','·','¸','¹','º','»','¼','½','¾','¿','À','Á','Â','Ã','Ä','Å','Æ','Ç','È','É','Ê','Ë','Ì','Í','Î','Ï','Ð','Ñ','Ò','Ó','Ô','Õ','Ö','×','Ø','Ù','Ú','Û','Ü','Ý','Þ','ß','à','á','â','ã','ä','å','æ','ç','è','é','ê','ë','ì','í','î','ï','ð','ñ','ò','ó','ô','õ','ö','÷','ø','ù','ú','û','ü','ý','þ','ÿ','"','&','<','>','Œ','œ','Š','š','Ÿ','ˆ','˜',' ',' ',' ','','','','','–','—','‘','’','‚','“','”','„','†','‡','‰','‹','›','€','ƒ','Α','Β','Γ','Δ','Ε','Ζ','Η','Θ','Ι','Κ','Λ','Μ','Ν','Ξ','Ο','Π','Ρ','Σ','Τ','Υ','Φ','Χ','Ψ','Ω','α','β','γ','δ','ε','ζ','η','θ','ι','κ','λ','μ','ν','ξ','ο','π','ρ','ς','σ','τ','υ','φ','χ','ψ','ω','ϑ','ϒ','ϖ','•','…','′','″','‾','⁄','℘','ℑ','ℜ','™','ℵ','←','↑','→','↓','↔','↵','⇐','⇑','⇒','⇓','⇔','∀','∂','∃','∅','∇','∈','∉','∋','∏','∑','−','∗','√','∝','∞','∠','∧','∨','∩','∪','∫','∴','∼','≅','≈','≠','≡','≤','≥','⊂','⊃','⊄','⊆','⊇','⊕','⊗','⊥','⋅','⌈','⌉','⌊','⌋','〈','〉','◊','♠','♣','♥','♦'],
46 |
47 | // Convert HTML entities into numerical entities
48 | HTML2Numerical : function(s){
49 | return this.swapArrayVals(s,this.arr1,this.arr2);
50 | },
51 |
52 | // Convert Numerical entities into HTML entities
53 | NumericalToHTML : function(s){
54 | return this.swapArrayVals(s,this.arr2,this.arr1);
55 | },
56 |
57 |
58 | // Numerically encodes all unicode characters
59 | numEncode : function(s){
60 | if(this.isEmpty(s)) return "";
61 |
62 | var a = [],
63 | l = s.length;
64 |
65 | for (var i=0;i "~"){
68 | a.push("");
69 | a.push(c.charCodeAt()); //numeric value of code point
70 | a.push(";");
71 | }else{
72 | a.push(c);
73 | }
74 | }
75 |
76 | return a.join("");
77 | },
78 |
79 | // HTML Decode numerical and HTML entities back to original values
80 | htmlDecode : function(s){
81 |
82 | var c,m,d = s;
83 |
84 | if(this.isEmpty(d)) return "";
85 |
86 | // convert HTML entites back to numerical entites first
87 | d = this.HTML2Numerical(d);
88 |
89 | // look for numerical entities "
90 | arr=d.match(/[0-9]{1,5};/g);
91 |
92 | // if no matches found in string then skip
93 | if(arr!=null){
94 | for(var x=0;x= -32768 && c <= 65535){
99 | // decode every single match within string
100 | d = d.replace(m, String.fromCharCode(c));
101 | }else{
102 | d = d.replace(m, ""); //invalid so replace with nada
103 | }
104 | }
105 | }
106 |
107 | return d;
108 | },
109 |
110 | // encode an input string into either numerical or HTML entities
111 | htmlEncode : function(s,dbl){
112 |
113 | if(this.isEmpty(s)) return "";
114 |
115 | // do we allow double encoding? E.g will & be turned into &
116 | dbl = dbl || false; //default to prevent double encoding
117 |
118 | // if allowing double encoding we do ampersands first
119 | if(dbl){
120 | if(this.EncodeType=="numerical"){
121 | s = s.replace(/&/g, "&");
122 | }else{
123 | s = s.replace(/&/g, "&");
124 | }
125 | }
126 |
127 | // convert the xss chars to numerical entities ' " < >
128 | s = this.XSSEncode(s,false);
129 |
130 | if(this.EncodeType=="numerical" || !dbl){
131 | // Now call function that will convert any HTML entities to numerical codes
132 | s = this.HTML2Numerical(s);
133 | }
134 |
135 | // Now encode all chars above 127 e.g unicode
136 | s = this.numEncode(s);
137 |
138 | // now we know anything that needs to be encoded has been converted to numerical entities we
139 | // can encode any ampersands & that are not part of encoded entities
140 | // to handle the fact that I need to do a negative check and handle multiple ampersands &&&
141 | // I am going to use a placeholder
142 |
143 | // if we don't want double encoded entities we ignore the & in existing entities
144 | if(!dbl){
145 | s = s.replace(//g,"##AMPHASH##");
146 |
147 | if(this.EncodeType=="numerical"){
148 | s = s.replace(/&/g, "&");
149 | }else{
150 | s = s.replace(/&/g, "&");
151 | }
152 |
153 | s = s.replace(/##AMPHASH##/g,"");
154 | }
155 |
156 | // replace any malformed entities
157 | s = s.replace(/\d*([^\d;]|$)/g, "$1");
158 |
159 | if(!dbl){
160 | // safety check to correct any double encoded &
161 | s = this.correctEncoding(s);
162 | }
163 |
164 | // now do we need to convert our numerical encoded string into entities
165 | if(this.EncodeType=="entity"){
166 | s = this.NumericalToHTML(s);
167 | }
168 |
169 | return s;
170 | },
171 |
172 | // Encodes the basic 4 characters used to malform HTML in XSS hacks
173 | XSSEncode : function(s,en){
174 | if(!this.isEmpty(s)){
175 | en = en || true;
176 | // do we convert to numerical or html entity?
177 | if(en){
178 | s = s.replace(/\'/g,"'"); //no HTML equivalent as &apos is not cross browser supported
179 | s = s.replace(/\"/g,""");
180 | s = s.replace(//g,">");
182 | }else{
183 | s = s.replace(/\'/g,"'"); //no HTML equivalent as &apos is not cross browser supported
184 | s = s.replace(/\"/g,""");
185 | s = s.replace(//g,">");
187 | }
188 | return s;
189 | }else{
190 | return "";
191 | }
192 | },
193 |
194 | // returns true if a string contains html or numerical encoded entities
195 | hasEncoded : function(s){
196 | if(/[0-9]{1,5};/g.test(s)){
197 | return true;
198 | }else if(/&[A-Z]{2,6};/gi.test(s)){
199 | return true;
200 | }else{
201 | return false;
202 | }
203 | },
204 |
205 | // will remove any unicode characters
206 | stripUnicode : function(s){
207 | return s.replace(/[^\x20-\x7E]/g,"");
208 |
209 | },
210 |
211 | // corrects any double encoded & entities e.g &
212 | correctEncoding : function(s){
213 | return s.replace(/(&)(amp;)+/,"$1");
214 | },
215 |
216 |
217 | // Function to loop through an array swaping each item with the value from another array e.g swap HTML entities with Numericals
218 | swapArrayVals : function(s,arr1,arr2){
219 | if(this.isEmpty(s)) return "";
220 | var re;
221 | if(arr1 && arr2){
222 | //ShowDebug("in swapArrayVals arr1.length = " + arr1.length + " arr2.length = " + arr2.length)
223 | // array lengths must match
224 | if(arr1.length == arr2.length){
225 | for(var x=0,i=arr1.length;x