diff --git a/src/core/components/auth/oauth2.jsx b/src/core/components/auth/oauth2.jsx
index aef95ac7..6c9cfde9 100644
--- a/src/core/components/auth/oauth2.jsx
+++ b/src/core/components/auth/oauth2.jsx
@@ -122,11 +122,13 @@ export default class Oauth2 extends React.Component {
const { isOAS3 } = specSelectors
+ let oidcUrl = isOAS3() ? schema.get("openIdConnectUrl") : null
+
// Auth type consts
const IMPLICIT = "implicit"
const PASSWORD = "password"
- const ACCESS_CODE = isOAS3() ? "authorizationCode" : "accessCode"
- const APPLICATION = isOAS3() ? "clientCredentials" : "application"
+ const ACCESS_CODE = isOAS3() ? (oidcUrl ? "authorization_code" : "authorizationCode") : "accessCode"
+ const APPLICATION = isOAS3() ? (oidcUrl ? "client_credentials" : "clientCredentials") : "application"
let flow = schema.get("flow")
let scopes = schema.get("allowedScopes") || schema.get("scopes")
@@ -144,6 +146,7 @@ export default class Oauth2 extends React.Component {
{ isAuthorized &&
Authorized
}
+ { oidcUrl && OpenID Connect URL: { oidcUrl }
}
{ ( flow === IMPLICIT || flow === ACCESS_CODE ) && Authorization URL: { schema.get("authorizationUrl") }
}
{ ( flow === PASSWORD || flow === ACCESS_CODE || flow === APPLICATION ) && Token URL: { schema.get("tokenUrl") }
}
Flow: { schema.get("flow") }
diff --git a/src/core/oauth2-authorize.js b/src/core/oauth2-authorize.js
index 03d62864..e8fcedd6 100644
--- a/src/core/oauth2-authorize.js
+++ b/src/core/oauth2-authorize.js
@@ -26,11 +26,13 @@ export default function authorize ( { auth, authActions, errActions, configs, au
break
case "clientCredentials":
+ case "client_credentials":
// OAS3
authActions.authorizeApplication(auth)
return
case "authorizationCode":
+ case "authorization_code":
// OAS3
query.push("response_type=code")
break
diff --git a/src/core/plugins/oas3/auth-extensions/wrap-selectors.js b/src/core/plugins/oas3/auth-extensions/wrap-selectors.js
index 25fd7adb..847db871 100644
--- a/src/core/plugins/oas3/auth-extensions/wrap-selectors.js
+++ b/src/core/plugins/oas3/auth-extensions/wrap-selectors.js
@@ -8,10 +8,13 @@ import { isOAS3 as isOAS3Helper } from "../helpers"
const state = state => state
function onlyOAS3(selector) {
- return (ori, system) => (state, ...args) => {
+ return (ori, system) => (...args) => {
const spec = system.getSystem().specSelectors.specJson()
if(isOAS3Helper(spec)) {
- return selector(system, ...args)
+ // Pass the spec plugin state to Reselect to trigger on securityDefinitions update
+ let resolvedSchemes = system.getState().getIn(["spec", "resolvedSubtrees",
+ "components", "securitySchemes"])
+ return selector(system, resolvedSchemes, ...args)
} else {
return ori(...args)
}
@@ -57,6 +60,32 @@ export const definitionsToAuthorize = onlyOAS3(createSelector(
[defName]: definition
}))
}
+ if(type === "openIdConnect" && definition.get("openIdConnectData")) {
+ let oidcData = definition.get("openIdConnectData")
+ let grants = oidcData.get("grant_types_supported") || ["authorization_code", "implicit"]
+ grants.forEach((grant) => {
+ // Convert from OIDC list of scopes to the OAS-style map with empty descriptions
+ let translatedScopes = oidcData.get("scopes_supported") &&
+ oidcData.get("scopes_supported").reduce((acc, cur) => acc.set(cur, ""), new Map())
+
+ let translatedDef = fromJS({
+ flow: grant,
+ authorizationUrl: oidcData.get("authorization_endpoint"),
+ tokenUrl: oidcData.get("token_endpoint"),
+ scopes: translatedScopes,
+ type: "oauth2",
+ openIdConnectUrl: definition.get("openIdConnectUrl")
+ })
+
+ list = list.push(new Map({
+ [defName]: translatedDef.filter((v) => {
+ // filter out unset values, sometimes `authorizationUrl`
+ // and `tokenUrl` come out as `undefined` in the data
+ return v !== undefined
+ })
+ }))
+ })
+ }
})
return list
diff --git a/src/core/plugins/spec/actions.js b/src/core/plugins/spec/actions.js
index 1585795c..37934126 100644
--- a/src/core/plugins/spec/actions.js
+++ b/src/core/plugins/spec/actions.js
@@ -150,6 +150,7 @@ const debResolveSubtrees = debounce(async () => {
errSelectors,
fn: {
resolveSubtree,
+ fetch,
AST = {}
},
specSelectors,
@@ -206,6 +207,28 @@ const debResolveSubtrees = debounce(async () => {
errActions.newThrownErrBatch(preparedErrors)
}
+ if (spec && specSelectors.isOAS3() && path[0] === "components" && path[1] === "securitySchemes") {
+ // Resolve OIDC URLs if present
+ await Promise.all(Object.values(spec)
+ .filter((scheme) => scheme.type === "openIdConnect")
+ .map(async (oidcScheme) => {
+ const req = {
+ url: oidcScheme.openIdConnectUrl,
+ requestInterceptor: requestInterceptor,
+ responseInterceptor: responseInterceptor
+ }
+ try {
+ const res = await fetch(req)
+ if (res instanceof Error || res.status >= 400) {
+ console.error(res.statusText + " " + req.url)
+ } else {
+ oidcScheme.openIdConnectData = JSON.parse(res.text)
+ }
+ } catch (e) {
+ console.error(e)
+ }
+ }))
+ }
set(resultMap, path, spec)
set(specWithCurrentSubtrees, path, spec)
diff --git a/test/unit/core/plugins/oas3/wrap-auth-selectors.js b/test/unit/core/plugins/oas3/wrap-auth-selectors.js
index 578f42b2..a2203b94 100644
--- a/test/unit/core/plugins/oas3/wrap-auth-selectors.js
+++ b/test/unit/core/plugins/oas3/wrap-auth-selectors.js
@@ -1,5 +1,5 @@
-import { fromJS } from "immutable"
+import { fromJS, Map } from "immutable"
import {
definitionsToAuthorize
} from "corePlugins/oas3/auth-extensions/wrap-selectors"
@@ -12,6 +12,7 @@ describe("oas3 plugin - auth extensions - wrapSelectors", function(){
// Given
const system = {
getSystem: () => system,
+ getState: () => new Map(),
specSelectors: {
specJson: () => fromJS({
openapi: "3.0.0"
@@ -53,7 +54,39 @@ describe("oas3 plugin - auth extensions - wrapSelectors", function(){
}
}
}
- }
+ },
+ "oidc": {
+ "type": "openIdConnect",
+ "openIdConnectUrl": "https://accounts.google.com/.well-known/openid-configuration",
+ "openIdConnectData": {
+ "authorization_endpoint": "https://accounts.google.com/o/oauth2/v2/auth",
+ "token_endpoint": "https://oauth2.googleapis.com/token",
+ "scopes_supported": [
+ "openid",
+ "email",
+ "profile"
+ ],
+ "grant_types_supported": [
+ "authorization_code",
+ "refresh_token",
+ "urn:ietf:params:oauth:grant-type:device_code",
+ "urn:ietf:params:oauth:grant-type:jwt-bearer"
+ ]
+ }
+ },
+ "oidcNoGrant": {
+ "type": "openIdConnect",
+ "openIdConnectUrl": "https://accounts.google.com/.well-known/openid-configuration",
+ "openIdConnectData": {
+ "authorization_endpoint": "https://accounts.google.com/o/oauth2/v2/auth",
+ "token_endpoint": "https://oauth2.googleapis.com/token",
+ "scopes_supported": [
+ "openid",
+ "email",
+ "profile"
+ ]
+ },
+ },
})
}
}
@@ -106,6 +139,96 @@ describe("oas3 plugin - auth extensions - wrapSelectors", function(){
type: "oauth2"
}
},
+ {
+ oidc: {
+ flow: "authorization_code",
+ authorizationUrl: "https://accounts.google.com/o/oauth2/v2/auth",
+ tokenUrl: "https://oauth2.googleapis.com/token",
+ openIdConnectUrl: "https://accounts.google.com/.well-known/openid-configuration",
+ scopes: {
+ "openid": "",
+ "email": "",
+ "profile": "",
+ },
+ type: "oauth2"
+ }
+ },
+ {
+ oidc: {
+ flow: "refresh_token",
+ authorizationUrl: "https://accounts.google.com/o/oauth2/v2/auth",
+ tokenUrl: "https://oauth2.googleapis.com/token",
+ openIdConnectUrl: "https://accounts.google.com/.well-known/openid-configuration",
+ scopes: {
+ "openid": "",
+ "email": "",
+ "profile": "",
+ },
+ type: "oauth2"
+ }
+ },
+ {
+ oidc: {
+ flow: "urn:ietf:params:oauth:grant-type:device_code",
+ authorizationUrl: "https://accounts.google.com/o/oauth2/v2/auth",
+ tokenUrl: "https://oauth2.googleapis.com/token",
+ openIdConnectUrl: "https://accounts.google.com/.well-known/openid-configuration",
+ scopes: {
+ "openid": "",
+ "email": "",
+ "profile": "",
+ },
+ type: "oauth2"
+ }
+ },
+ {
+ oidc: {
+ flow: "urn:ietf:params:oauth:grant-type:jwt-bearer",
+ authorizationUrl: "https://accounts.google.com/o/oauth2/v2/auth",
+ tokenUrl: "https://oauth2.googleapis.com/token",
+ openIdConnectUrl: "https://accounts.google.com/.well-known/openid-configuration",
+ scopes: {
+ "openid": "",
+ "email": "",
+ "profile": "",
+ },
+ type: "oauth2"
+ }
+ },
+ {
+ // See https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
+ // grant_types_supported
+ // OPTIONAL. JSON array containing a list of the OAuth 2.0 Grant Type values that
+ // this OP supports. Dynamic OpenID Providers MUST support the authorization_code
+ // and implicit Grant Type values and MAY support other Grant Types. If omitted,
+ // the default value is ["authorization_code", "implicit"].
+ oidcNoGrant: {
+ flow: "authorization_code",
+ authorizationUrl: "https://accounts.google.com/o/oauth2/v2/auth",
+ tokenUrl: "https://oauth2.googleapis.com/token",
+ openIdConnectUrl: "https://accounts.google.com/.well-known/openid-configuration",
+ scopes: {
+ "openid": "",
+ "email": "",
+ "profile": "",
+ },
+ type: "oauth2"
+ }
+ },
+ {
+ oidcNoGrant: {
+ flow: "implicit",
+ authorizationUrl: "https://accounts.google.com/o/oauth2/v2/auth",
+ tokenUrl: "https://oauth2.googleapis.com/token",
+ openIdConnectUrl: "https://accounts.google.com/.well-known/openid-configuration",
+ scopes: {
+ "openid": "",
+ "email": "",
+ "profile": "",
+ },
+ type: "oauth2"
+ }
+ },
])
})