feat(auth): persist cookie based apiKey in document.cookie (#8689)
Refs #8683
This commit is contained in:
@@ -273,11 +273,12 @@ export function restoreAuthorization(payload) {
|
||||
|
||||
export const persistAuthorizationIfNeeded = () => ( { authSelectors, getConfigs } ) => {
|
||||
const configs = getConfigs()
|
||||
if (configs.persistAuthorization)
|
||||
{
|
||||
const authorized = authSelectors.authorized()
|
||||
localStorage.setItem("authorized", JSON.stringify(authorized.toJS()))
|
||||
}
|
||||
|
||||
if (!configs.persistAuthorization) return
|
||||
|
||||
// persist authorization to local storage
|
||||
const authorized = authSelectors.authorized().toJS()
|
||||
localStorage.setItem("authorized", JSON.stringify(authorized))
|
||||
}
|
||||
|
||||
export const authPopup = (url, swaggerUIRedirectOauth2) => ( ) => {
|
||||
|
||||
19
src/core/plugins/auth/configs-extensions/wrap-actions.js
Normal file
19
src/core/plugins/auth/configs-extensions/wrap-actions.js
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
export const loaded = (oriAction, system) => (payload) => {
|
||||
const { getConfigs, authActions } = system
|
||||
const configs = getConfigs()
|
||||
|
||||
oriAction(payload)
|
||||
|
||||
// check if we should restore authorization data from localStorage
|
||||
if (configs.persistAuthorization) {
|
||||
const authorized = localStorage.getItem("authorized")
|
||||
if (authorized) {
|
||||
authActions.restoreAuthorization({
|
||||
authorized: JSON.parse(authorized),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
import reducers from "./reducers"
|
||||
import * as actions from "./actions"
|
||||
import * as selectors from "./selectors"
|
||||
import * as specWrapActionReplacements from "./spec-wrap-actions"
|
||||
import { execute as wrappedExecuteAction } from "./spec-extensions/wrap-actions"
|
||||
import { loaded as wrappedLoadedAction } from "./configs-extensions/wrap-actions"
|
||||
import { authorize as wrappedAuthorizeAction, logout as wrappedLogoutAction } from "./wrap-actions"
|
||||
|
||||
export default function() {
|
||||
return {
|
||||
@@ -15,11 +17,22 @@ export default function() {
|
||||
auth: {
|
||||
reducers,
|
||||
actions,
|
||||
selectors
|
||||
selectors,
|
||||
wrapActions: {
|
||||
authorize: wrappedAuthorizeAction,
|
||||
logout: wrappedLogoutAction,
|
||||
}
|
||||
},
|
||||
configs: {
|
||||
wrapActions: {
|
||||
loaded: wrappedLoadedAction,
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
wrapActions: specWrapActionReplacements
|
||||
}
|
||||
wrapActions: {
|
||||
execute: wrappedExecuteAction,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
64
src/core/plugins/auth/wrap-actions.js
Normal file
64
src/core/plugins/auth/wrap-actions.js
Normal file
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
/**
|
||||
* `authorize` and `logout` wrapped actions provide capacity
|
||||
* to persist cookie based apiKey in document.cookie.
|
||||
*
|
||||
* `persistAuthorization` SwaggerUI options needs to set to `true`
|
||||
* for document.cookie persistence to work.
|
||||
*/
|
||||
export const authorize = (oriAction, system) => (payload) => {
|
||||
oriAction(payload)
|
||||
|
||||
const configs = system.getConfigs()
|
||||
|
||||
if (!configs.persistAuthorization) return
|
||||
|
||||
// create cookie
|
||||
try {
|
||||
const [{ schema, value }] = Object.values(payload)
|
||||
const isApiKeyAuth = schema.get("type") === "apiKey"
|
||||
const isInCookie = schema.get("in") === "cookie"
|
||||
const isApiKeyInCookie = isApiKeyAuth && isInCookie
|
||||
|
||||
if (isApiKeyInCookie) {
|
||||
document.cookie = `${schema.get("name")}=${value}; SameSite=None; Secure`
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"Error persisting cookie based apiKey in document.cookie.",
|
||||
error
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export const logout = (oriAction, system) => (payload) => {
|
||||
const configs = system.getConfigs()
|
||||
const authorized = system.authSelectors.authorized()
|
||||
|
||||
// deleting cookie
|
||||
try {
|
||||
if (configs.persistAuthorization && Array.isArray(payload)) {
|
||||
payload.forEach((authorizedName) => {
|
||||
const auth = authorized.get(authorizedName, {})
|
||||
const isApiKeyAuth = auth.getIn(["schema", "type"]) === "apiKey"
|
||||
const isInCookie = auth.getIn(["schema", "in"]) === "cookie"
|
||||
const isApiKeyInCookie = isApiKeyAuth && isInCookie
|
||||
|
||||
if (isApiKeyInCookie) {
|
||||
const cookieName = auth.getIn(["schema", "name"])
|
||||
document.cookie = `${cookieName}=; Max-Age=-99999999`
|
||||
}
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"Error deleting cookie based apiKey from document.cookie.",
|
||||
error
|
||||
)
|
||||
}
|
||||
|
||||
oriAction(payload)
|
||||
}
|
||||
@@ -21,17 +21,6 @@ export function toggle(configName) {
|
||||
|
||||
|
||||
// Hook
|
||||
export const loaded = () => ({getConfigs, authActions}) => {
|
||||
// check if we should restore authorization data from localStorage
|
||||
const configs = getConfigs()
|
||||
if (configs.persistAuthorization)
|
||||
{
|
||||
const authorized = localStorage.getItem("authorized")
|
||||
if(authorized)
|
||||
{
|
||||
authActions.restoreAuthorization({
|
||||
authorized: JSON.parse(authorized)
|
||||
})
|
||||
}
|
||||
}
|
||||
export const loaded = () => () => {
|
||||
// noop
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { loaded } from "corePlugins/auth/configs-extensions/wrap-actions"
|
||||
|
||||
describe("loaded hook", () => {
|
||||
describe("authorization data restoration", () => {
|
||||
beforeEach(() => {
|
||||
localStorage.clear()
|
||||
})
|
||||
it("retrieve `authorized` value from `localStorage`", () => {
|
||||
const system = {
|
||||
getConfigs: () => ({
|
||||
persistAuthorization: true
|
||||
}),
|
||||
authActions: {
|
||||
|
||||
}
|
||||
}
|
||||
jest.spyOn(Object.getPrototypeOf(window.localStorage), "getItem")
|
||||
|
||||
loaded(jest.fn(), system)()
|
||||
expect(localStorage.getItem).toHaveBeenCalled()
|
||||
expect(localStorage.getItem).toHaveBeenCalledWith("authorized")
|
||||
})
|
||||
it("restore authorization data when a value exists", () => {
|
||||
const system = {
|
||||
getConfigs: () => ({
|
||||
persistAuthorization: true
|
||||
}),
|
||||
authActions: {
|
||||
restoreAuthorization: jest.fn(() => {})
|
||||
}
|
||||
}
|
||||
const mockData = {"api_key": {}}
|
||||
localStorage.setItem("authorized", JSON.stringify(mockData))
|
||||
loaded(jest.fn(), system)()
|
||||
expect(system.authActions.restoreAuthorization).toHaveBeenCalled()
|
||||
expect(system.authActions.restoreAuthorization).toHaveBeenCalledWith({
|
||||
authorized: mockData
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,5 +1,4 @@
|
||||
|
||||
import { execute } from "corePlugins/auth/spec-wrap-actions"
|
||||
import { execute } from "corePlugins/auth/spec-extensions/wrap-actions"
|
||||
|
||||
describe("spec plugin - actions", function(){
|
||||
|
||||
119
test/unit/core/plugins/auth/wrap-actions.js
Normal file
119
test/unit/core/plugins/auth/wrap-actions.js
Normal file
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
import { fromJS } from "immutable"
|
||||
import { authorize, logout } from "corePlugins/auth/wrap-actions"
|
||||
|
||||
describe("Cookie based apiKey persistence in document.cookie", () => {
|
||||
beforeEach(() => {
|
||||
let cookieJar = ""
|
||||
jest.spyOn(document, "cookie", "set").mockImplementation((cookie) => {
|
||||
cookieJar += cookie
|
||||
})
|
||||
jest.spyOn(document, "cookie", "get").mockImplementation(() => cookieJar)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
jest.restoreAllMocks()
|
||||
})
|
||||
|
||||
describe("given persistAuthorization=true", () => {
|
||||
it("should persist cookie in document.cookie", () => {
|
||||
const system = {
|
||||
getConfigs: () => ({
|
||||
persistAuthorization: true,
|
||||
}),
|
||||
}
|
||||
const payload = {
|
||||
api_key: {
|
||||
schema: fromJS({
|
||||
type: "apiKey",
|
||||
name: "apiKeyCookie",
|
||||
in: "cookie",
|
||||
}),
|
||||
value: "test",
|
||||
},
|
||||
}
|
||||
|
||||
authorize(jest.fn(), system)(payload)
|
||||
|
||||
expect(document.cookie).toEqual(
|
||||
"apiKeyCookie=test; SameSite=None; Secure"
|
||||
)
|
||||
})
|
||||
|
||||
it("should delete cookie from document.cookie", () => {
|
||||
const payload = fromJS({
|
||||
api_key: {
|
||||
schema: {
|
||||
type: "apiKey",
|
||||
name: "apiKeyCookie",
|
||||
in: "cookie",
|
||||
},
|
||||
value: "test",
|
||||
},
|
||||
})
|
||||
const system = {
|
||||
getConfigs: () => ({
|
||||
persistAuthorization: true,
|
||||
}),
|
||||
authSelectors: {
|
||||
authorized: () => payload,
|
||||
},
|
||||
}
|
||||
|
||||
logout(jest.fn(), system)(["api_key"])
|
||||
|
||||
expect(document.cookie).toEqual("apiKeyCookie=; Max-Age=-99999999")
|
||||
})
|
||||
})
|
||||
|
||||
describe("given persistAuthorization=false", () => {
|
||||
it("shouldn't persist cookie in document.cookie", () => {
|
||||
const system = {
|
||||
getConfigs: () => ({
|
||||
persistAuthorization: false,
|
||||
}),
|
||||
}
|
||||
const payload = {
|
||||
api_key: {
|
||||
schema: fromJS({
|
||||
type: "apiKey",
|
||||
name: "apiKeyCookie",
|
||||
in: "cookie",
|
||||
}),
|
||||
value: "test",
|
||||
},
|
||||
}
|
||||
|
||||
authorize(jest.fn(), system)(payload)
|
||||
|
||||
expect(document.cookie).toEqual("")
|
||||
})
|
||||
|
||||
it("should delete cookie from document.cookie", () => {
|
||||
const payload = fromJS({
|
||||
api_key: {
|
||||
schema: {
|
||||
type: "apiKey",
|
||||
name: "apiKeyCookie",
|
||||
in: "cookie",
|
||||
},
|
||||
value: "test",
|
||||
},
|
||||
})
|
||||
const system = {
|
||||
getConfigs: () => ({
|
||||
persistAuthorization: false,
|
||||
}),
|
||||
authSelectors: {
|
||||
authorized: () => payload,
|
||||
},
|
||||
}
|
||||
|
||||
logout(jest.fn(), system)(["api_key"])
|
||||
|
||||
expect(document.cookie).toEqual("")
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,5 +1,4 @@
|
||||
import { downloadConfig } from "corePlugins/configs/spec-actions"
|
||||
import { loaded } from "corePlugins/configs/actions"
|
||||
|
||||
describe("configs plugin - actions", () => {
|
||||
|
||||
@@ -23,43 +22,4 @@ describe("configs plugin - actions", () => {
|
||||
expect(fetchSpy).toHaveBeenCalledWith(req)
|
||||
})
|
||||
})
|
||||
|
||||
describe("loaded hook", () => {
|
||||
describe("authorization data restoration", () => {
|
||||
beforeEach(() => {
|
||||
localStorage.clear()
|
||||
})
|
||||
it("retrieve `authorized` value from `localStorage`", () => {
|
||||
const system = {
|
||||
getConfigs: () => ({
|
||||
persistAuthorization: true
|
||||
}),
|
||||
authActions: {
|
||||
|
||||
}
|
||||
}
|
||||
jest.spyOn(Object.getPrototypeOf(window.localStorage), "getItem")
|
||||
loaded()(system)
|
||||
expect(localStorage.getItem).toHaveBeenCalled()
|
||||
expect(localStorage.getItem).toHaveBeenCalledWith("authorized")
|
||||
})
|
||||
it("restore authorization data when a value exists", () => {
|
||||
const system = {
|
||||
getConfigs: () => ({
|
||||
persistAuthorization: true
|
||||
}),
|
||||
authActions: {
|
||||
restoreAuthorization: jest.fn(() => {})
|
||||
}
|
||||
}
|
||||
const mockData = {"api_key": {}}
|
||||
localStorage.setItem("authorized", JSON.stringify(mockData))
|
||||
loaded()(system)
|
||||
expect(system.authActions.restoreAuthorization).toHaveBeenCalled()
|
||||
expect(system.authActions.restoreAuthorization).toHaveBeenCalledWith({
|
||||
authorized: mockData
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user