From 351afde43f0eb2ed8a52b327059d00ac6c1fcb09 Mon Sep 17 00:00:00 2001 From: Sayooj Sojen Date: Mon, 5 Apr 2021 22:33:34 +0530 Subject: [PATCH] Expose identity token for oauth2 (#3211) --- .../app/common/__tests__/misc.test.js | 22 ++++++- packages/insomnia-app/app/common/misc.js | 10 ++++ .../insomnia-app/app/models/o-auth-2-token.js | 2 + .../grant-authorization-code.test.js | 3 + .../grant-client-credentials.test.js | 2 + .../o-auth-2/__tests__/grant-implicit.test.js | 1 + .../o-auth-2/__tests__/grant-password.test.js | 2 + .../app/network/o-auth-2/get-token.js | 3 +- .../o-auth-2/grant-authorization-code.js | 1 + .../o-auth-2/grant-client-credentials.js | 1 + .../app/network/o-auth-2/grant-implicit.js | 1 - .../app/network/o-auth-2/grant-password.js | 1 + .../app/network/o-auth-2/refresh-token.js | 1 + .../components/editors/auth/o-auth-2-auth.js | 57 +++++++++++++++++-- plugins/insomnia-plugin-request/index.js | 40 ++++++++++--- 15 files changed, 133 insertions(+), 14 deletions(-) diff --git a/packages/insomnia-app/app/common/__tests__/misc.test.js b/packages/insomnia-app/app/common/__tests__/misc.test.js index 34f9b9a267..48d6fd705d 100644 --- a/packages/insomnia-app/app/common/__tests__/misc.test.js +++ b/packages/insomnia-app/app/common/__tests__/misc.test.js @@ -1,6 +1,12 @@ import * as misc from '../misc'; import { globalBeforeEach } from '../../__jest__/before-each'; -import { diffPatchObj, isNotNullOrUndefined, pluralize, snapNumberToLimits } from '../misc'; +import { + diffPatchObj, + isNotNullOrUndefined, + pluralize, + snapNumberToLimits, + convertEpochToMilliseconds, +} from '../misc'; describe('hasAuthHeader()', () => { beforeEach(globalBeforeEach); @@ -248,6 +254,20 @@ describe('diffPatchObj()', () => { }); }); +describe('convertEpochToMilliseconds()', () => { + it('should convert microseconds to milliseconds', () => { + expect(convertEpochToMilliseconds(1617616858412123)).toBe(1617616858412); + }); + + it('should convert seconds to milliseconds', () => { + expect(convertEpochToMilliseconds(1617617010)).toBe(1617617010000); + }); + + it('should output same if value already in milliseconds', () => { + expect(convertEpochToMilliseconds(1617617141412)).toBe(1617617141412); + }); +}); + describe('snapNumberToLimits()', () => { it('should return value', () => { expect(snapNumberToLimits(2)).toBe(2); diff --git a/packages/insomnia-app/app/common/misc.js b/packages/insomnia-app/app/common/misc.js index e8f4563467..78e59ed068 100644 --- a/packages/insomnia-app/app/common/misc.js +++ b/packages/insomnia-app/app/common/misc.js @@ -441,6 +441,16 @@ export function isObject(obj: any) { return obj !== null && typeof obj === 'object'; } +export function convertEpochToMilliseconds(epoch: number) { + /* + Finds epoch's digit count and converts it to make it exactly 13 digits. + Which is the epoch millisecond represntation. + */ + const expDigitCount = epoch.toString().length; + const convertedEpoch = parseInt(epoch * 10 ** (13 - expDigitCount)); + return convertedEpoch; +} + export function snapNumberToLimits(value: number, min?: number, max?: number): number { const moreThanMax = max && !Number.isNaN(max) && value > max; const lessThanMin = min && !Number.isNaN(min) && value < min; diff --git a/packages/insomnia-app/app/models/o-auth-2-token.js b/packages/insomnia-app/app/models/o-auth-2-token.js index e27ea311b6..7df12e31ef 100644 --- a/packages/insomnia-app/app/models/o-auth-2-token.js +++ b/packages/insomnia-app/app/models/o-auth-2-token.js @@ -6,6 +6,7 @@ import * as db from '../common/database'; type BaseOAuth2Token = { refreshToken: string, accessToken: string, + identityToken: string, expiresAt: number | null, // Should be Date.now() if valid // Debug @@ -30,6 +31,7 @@ export function init(): BaseOAuth2Token { return { refreshToken: '', accessToken: '', + identityToken: '', expiresAt: null, // Should be Date.now() if valid // Debug diff --git a/packages/insomnia-app/app/network/o-auth-2/__tests__/grant-authorization-code.test.js b/packages/insomnia-app/app/network/o-auth-2/__tests__/grant-authorization-code.test.js index b44ab73ef6..fe7c8700d8 100644 --- a/packages/insomnia-app/app/network/o-auth-2/__tests__/grant-authorization-code.test.js +++ b/packages/insomnia-app/app/network/o-auth-2/__tests__/grant-authorization-code.test.js @@ -95,6 +95,7 @@ describe('authorization_code', () => { // Check the expected value expect(result).toEqual({ access_token: 'token_123', + id_token: null, refresh_token: null, expires_in: null, token_type: 'token_type', @@ -182,6 +183,7 @@ describe('authorization_code', () => { // Check the expected value expect(result).toEqual({ access_token: 'token_123', + id_token: null, refresh_token: null, expires_in: null, token_type: 'token_type', @@ -241,6 +243,7 @@ describe('authorization_code', () => { // Check the expected value expect(result).toEqual({ access_token: 'token_123', + id_token: null, refresh_token: null, expires_in: null, token_type: 'token_type', diff --git a/packages/insomnia-app/app/network/o-auth-2/__tests__/grant-client-credentials.test.js b/packages/insomnia-app/app/network/o-auth-2/__tests__/grant-client-credentials.test.js index e5e809deb4..cea7fd23ed 100644 --- a/packages/insomnia-app/app/network/o-auth-2/__tests__/grant-client-credentials.test.js +++ b/packages/insomnia-app/app/network/o-auth-2/__tests__/grant-client-credentials.test.js @@ -85,6 +85,7 @@ describe('client_credentials', () => { // Check the expected value expect(result).toEqual({ access_token: 'token_123', + id_token: null, expires_in: null, token_type: 'token_type', scope: SCOPE, @@ -165,6 +166,7 @@ describe('client_credentials', () => { // Check the expected value expect(result).toEqual({ access_token: 'token_123', + id_token: null, expires_in: null, token_type: 'token_type', scope: SCOPE, diff --git a/packages/insomnia-app/app/network/o-auth-2/__tests__/grant-implicit.test.js b/packages/insomnia-app/app/network/o-auth-2/__tests__/grant-implicit.test.js index dd99d1a1d3..8fc6e7b7d7 100644 --- a/packages/insomnia-app/app/network/o-auth-2/__tests__/grant-implicit.test.js +++ b/packages/insomnia-app/app/network/o-auth-2/__tests__/grant-implicit.test.js @@ -19,6 +19,7 @@ describe('implicit', () => { expect(result).toEqual({ access_token: 'token_123', + id_token: null, token_type: null, expires_in: null, scope: null, diff --git a/packages/insomnia-app/app/network/o-auth-2/__tests__/grant-password.test.js b/packages/insomnia-app/app/network/o-auth-2/__tests__/grant-password.test.js index e4adee0e56..7860d43226 100644 --- a/packages/insomnia-app/app/network/o-auth-2/__tests__/grant-password.test.js +++ b/packages/insomnia-app/app/network/o-auth-2/__tests__/grant-password.test.js @@ -87,6 +87,7 @@ describe('password', () => { // Check the expected value expect(result).toEqual({ access_token: 'token_123', + id_token: null, expires_in: null, token_type: 'token_type', refresh_token: null, @@ -168,6 +169,7 @@ describe('password', () => { // Check the expected value expect(result).toEqual({ access_token: 'token_123', + id_token: null, expires_in: null, token_type: 'token_type', refresh_token: null, diff --git a/packages/insomnia-app/app/network/o-auth-2/get-token.js b/packages/insomnia-app/app/network/o-auth-2/get-token.js index a4a33d84e8..8ff98f19d2 100644 --- a/packages/insomnia-app/app/network/o-auth-2/get-token.js +++ b/packages/insomnia-app/app/network/o-auth-2/get-token.js @@ -15,6 +15,7 @@ import { P_ERROR, P_EXPIRES_IN, P_REFRESH_TOKEN, + P_ID_TOKEN, X_RESPONSE_ID, X_ERROR, } from './constants'; @@ -92,7 +93,6 @@ async function _getOAuth2ClientCredentialsHeader( authentication.audience, authentication.resource, ); - return _updateOAuth2Token(requestId, results); } @@ -218,6 +218,7 @@ async function _updateOAuth2Token(requestId: string, authResults: Object): Promi expiresAt, refreshToken: authResults[P_REFRESH_TOKEN] || null, accessToken: authResults[P_ACCESS_TOKEN] || null, + identityToken: authResults[P_ID_TOKEN] || null, error: authResults[P_ERROR] || null, errorDescription: authResults[P_ERROR_DESCRIPTION] || null, errorUri: authResults[P_ERROR_URI] || null, diff --git a/packages/insomnia-app/app/network/o-auth-2/grant-authorization-code.js b/packages/insomnia-app/app/network/o-auth-2/grant-authorization-code.js index f1380c66b5..8af812041c 100644 --- a/packages/insomnia-app/app/network/o-auth-2/grant-authorization-code.js +++ b/packages/insomnia-app/app/network/o-auth-2/grant-authorization-code.js @@ -192,6 +192,7 @@ async function _getToken( const results = responseToObject(bodyBuffer.toString('utf8'), [ c.P_ACCESS_TOKEN, + c.P_ID_TOKEN, c.P_REFRESH_TOKEN, c.P_EXPIRES_IN, c.P_TOKEN_TYPE, diff --git a/packages/insomnia-app/app/network/o-auth-2/grant-client-credentials.js b/packages/insomnia-app/app/network/o-auth-2/grant-client-credentials.js index a4f0a03e4b..c4e68ca691 100644 --- a/packages/insomnia-app/app/network/o-auth-2/grant-client-credentials.js +++ b/packages/insomnia-app/app/network/o-auth-2/grant-client-credentials.js @@ -67,6 +67,7 @@ export default async function( const results = responseToObject(bodyBuffer.toString('utf8'), [ c.P_ACCESS_TOKEN, + c.P_ID_TOKEN, c.P_TOKEN_TYPE, c.P_EXPIRES_IN, c.P_SCOPE, diff --git a/packages/insomnia-app/app/network/o-auth-2/grant-implicit.js b/packages/insomnia-app/app/network/o-auth-2/grant-implicit.js index 16677353ac..adbd4eba25 100644 --- a/packages/insomnia-app/app/network/o-auth-2/grant-implicit.js +++ b/packages/insomnia-app/app/network/o-auth-2/grant-implicit.js @@ -56,7 +56,6 @@ export default async function( c.P_ERROR_URI, ]); results[c.P_ACCESS_TOKEN] = results[c.P_ACCESS_TOKEN] || results[c.P_ID_TOKEN]; - delete results[c.P_ID_TOKEN]; return results; } else { // Bad redirect diff --git a/packages/insomnia-app/app/network/o-auth-2/grant-password.js b/packages/insomnia-app/app/network/o-auth-2/grant-password.js index 1f49047bd3..dfd4635ffb 100644 --- a/packages/insomnia-app/app/network/o-auth-2/grant-password.js +++ b/packages/insomnia-app/app/network/o-auth-2/grant-password.js @@ -71,6 +71,7 @@ export default async function( const results = responseToObject(bodyBuffer.toString(), [ c.P_ACCESS_TOKEN, + c.P_ID_TOKEN, c.P_TOKEN_TYPE, c.P_EXPIRES_IN, c.P_REFRESH_TOKEN, diff --git a/packages/insomnia-app/app/network/o-auth-2/refresh-token.js b/packages/insomnia-app/app/network/o-auth-2/refresh-token.js index 877d5b0209..989fd245de 100644 --- a/packages/insomnia-app/app/network/o-auth-2/refresh-token.js +++ b/packages/insomnia-app/app/network/o-auth-2/refresh-token.js @@ -80,6 +80,7 @@ export default async function( bodyBuffer.toString(), [ c.P_ACCESS_TOKEN, + c.P_ID_TOKEN, c.P_REFRESH_TOKEN, c.P_EXPIRES_IN, c.P_TOKEN_TYPE, diff --git a/packages/insomnia-app/app/ui/components/editors/auth/o-auth-2-auth.js b/packages/insomnia-app/app/ui/components/editors/auth/o-auth-2-auth.js index 8dc725a5e7..27f96b84e2 100644 --- a/packages/insomnia-app/app/ui/components/editors/auth/o-auth-2-auth.js +++ b/packages/insomnia-app/app/ui/components/editors/auth/o-auth-2-auth.js @@ -29,6 +29,7 @@ import { showModal } from '../../modals'; import ResponseDebugModal from '../../modals/response-debug-modal'; import type { Settings } from '../../../../models/settings'; import { initNewOAuthSession } from '../../../../network/o-auth-2/misc'; +import { convertEpochToMilliseconds } from '../../../../common/misc'; type Props = { handleRender: Function, @@ -87,6 +88,20 @@ class OAuth2Auth extends React.PureComponent { } } + async _handleUpdateIdentityToken(e: SyntheticEvent): Promise { + const { oAuth2Token } = this.props; + const identityToken = e.currentTarget.value; + + if (oAuth2Token) { + await models.oAuth2Token.update(oAuth2Token, { identityToken }); + } else { + await models.oAuth2Token.create({ + identityToken, + parentId: this.props.request._id, + }); + } + } + async _handleUpdateRefreshToken(e: SyntheticEvent): Promise { const { oAuth2Token } = this.props; const refreshToken = e.currentTarget.value; @@ -483,7 +498,31 @@ class OAuth2Auth extends React.PureComponent { return { basic: basicFields, advanced: advancedFields }; } - static renderExpireAt(token: ?OAuth2Token): React.Element<*> | string | null { + static renderIdentityTokenExpiry(token: ?OAuth2Token): React.Element<*> | string | null { + if (!token || !token.identityToken) { + return null; + } + const base64Url = token.identityToken.split('.')[1]; + let decodedString = ''; + try { + decodedString = window.atob(base64Url); + } catch (error) { + return null; + } + const { exp } = JSON.parse(decodedString); + if (!exp) { + return '(never expires)'; + } + const convertedExp = convertEpochToMilliseconds(exp); + return ( + + (expires + ) + + ); + } + + static renderAccessTokenExpiry(token: ?OAuth2Token): React.Element<*> | string | null { if (!token || !token.accessToken) { return null; } @@ -491,7 +530,6 @@ class OAuth2Auth extends React.PureComponent { if (!token.expiresAt) { return '(never expires)'; } - return ( (expires @@ -541,7 +579,8 @@ class OAuth2Auth extends React.PureComponent { const { request, oAuth2Token: tok } = this.props; const { loading, error, showAdvanced } = this.state; - const expireLabel = OAuth2Auth.renderExpireAt(tok); + const accessExpireLabel = OAuth2Auth.renderAccessTokenExpiry(tok); + const identityExpireLabel = OAuth2Auth.renderIdentityTokenExpiry(tok); const fields = this.renderGrantTypeFields(request.authentication.grantType); return ( @@ -608,7 +647,17 @@ class OAuth2Auth extends React.PureComponent {
+
+
+