+ ( (flow === APPLICATION || flow === ACCESS_CODE || flow === PASSWORD) &&
{
isAuthorized ? ******
@@ -197,7 +196,7 @@ export default class Oauth2 extends React.Component {
}
- }
+ )}
{
!isAuthorized && scopes && scopes.size ?
diff --git a/src/core/components/live-response.jsx b/src/core/components/live-response.jsx
index 8cc93288..527e24e3 100644
--- a/src/core/components/live-response.jsx
+++ b/src/core/components/live-response.jsx
@@ -80,7 +80,7 @@ export default class LiveResponse extends React.Component {
}
Server response
-
+
| Code |
diff --git a/src/core/plugins/auth/actions.js b/src/core/plugins/auth/actions.js
index 45dbf727..35afbc0d 100644
--- a/src/core/plugins/auth/actions.js
+++ b/src/core/plugins/auth/actions.js
@@ -74,28 +74,23 @@ export const authorizePassword = ( auth ) => ( { authActions } ) => {
let { schema, name, username, password, passwordType, clientId, clientSecret } = auth
let form = {
grant_type: "password",
- scope: auth.scopes.join(scopeSeparator)
+ scope: auth.scopes.join(scopeSeparator),
+ username,
+ password
}
let query = {}
let headers = {}
- if ( passwordType === "basic") {
- headers.Authorization = "Basic " + btoa(username + ":" + password)
- } else {
- Object.assign(form, {username}, {password})
+ switch (passwordType) {
+ case "request-body":
+ setClientIdAndSecret(form, clientId, clientSecret)
+ break
- switch ( passwordType ) {
- case "query":
- setClientIdAndSecret(query, clientId, clientSecret)
- break
-
- case "request-body":
- setClientIdAndSecret(form, clientId, clientSecret)
- break
-
- default:
- headers.Authorization = "Basic " + btoa(clientId + ":" + clientSecret)
- }
+ case "basic":
+ headers.Authorization = "Basic " + btoa(clientId + ":" + clientSecret)
+ break
+ default:
+ console.warn(`Warning: invalid passwordType ${passwordType} was passed, not including client id and secret`)
}
return authActions.authorizeRequest({ body: buildFormData(form), url: schema.get("tokenUrl"), name, headers, query, auth})
diff --git a/test/e2e-cypress/tests/.eslintrc b/test/e2e-cypress/.eslintrc
similarity index 100%
rename from test/e2e-cypress/tests/.eslintrc
rename to test/e2e-cypress/.eslintrc
diff --git a/test/e2e-cypress/helpers/oauth2-server/index.js b/test/e2e-cypress/helpers/oauth2-server/index.js
new file mode 100644
index 00000000..ba779352
--- /dev/null
+++ b/test/e2e-cypress/helpers/oauth2-server/index.js
@@ -0,0 +1,50 @@
+// from https://github.com/pedroetb/node-oauth2-server-example
+
+var Http = require("http")
+var path = require("path")
+var express = require("express")
+var bodyParser = require("body-parser")
+var oauthserver = require("oauth2-server")
+var cors = require("cors")
+
+var app = express()
+
+app.use(cors())
+
+app.use(bodyParser.urlencoded({ extended: true }))
+
+app.use(bodyParser.json())
+
+app.oauth = oauthserver({
+ model: require("./model.js"),
+ grants: ["password", "client_credentials", "implicit"],
+ debug: true
+})
+
+app.all("/oauth/token", app.oauth.grant())
+
+app.get("/swagger.yaml", function (req, res) {
+ res.sendFile(path.join(__dirname, "swagger.yaml"))
+})
+
+app.get("*", app.oauth.authorise(), function (req, res) {
+ res.send("Secret secrets are no fun, secret secrets hurt someone.")
+})
+
+app.use(app.oauth.errorHandler())
+
+function startServer() {
+ var httpServer = Http.createServer(app)
+ httpServer.listen("3231")
+
+ return function stopServer() {
+ httpServer.close()
+ }
+}
+
+module.exports = startServer
+
+if (require.main === module) {
+ // for debugging
+ startServer()
+}
\ No newline at end of file
diff --git a/test/e2e-cypress/helpers/oauth2-server/model.js b/test/e2e-cypress/helpers/oauth2-server/model.js
new file mode 100644
index 00000000..f5722bf4
--- /dev/null
+++ b/test/e2e-cypress/helpers/oauth2-server/model.js
@@ -0,0 +1,141 @@
+// from https://github.com/pedroetb/node-oauth2-server-example
+
+var config = {
+ clients: [{
+ clientId: "application",
+ clientSecret: "secret"
+ }],
+ confidentialClients: [{
+ clientId: "confidentialApplication",
+ clientSecret: "topSecret"
+ }],
+ tokens: [],
+ users: [{
+ id: "123",
+ username: "swagger",
+ password: "password"
+ }]
+}
+
+/**
+ * Dump the memory storage content (for debug).
+ */
+
+var dump = function () {
+
+ console.log("clients", config.clients)
+ console.log("confidentialClients", config.confidentialClients)
+ console.log("tokens", config.tokens)
+ console.log("users", config.users)
+}
+
+/*
+ * Methods used by all grant types.
+ */
+
+var getAccessToken = function (bearerToken, callback) {
+
+ var tokens = config.tokens.filter(function (token) {
+
+ return token.accessToken === bearerToken
+ })
+
+ return callback(false, tokens[0])
+}
+
+var getClient = function (clientId, clientSecret, callback) {
+
+ var clients = config.clients.filter(function (client) {
+
+ return client.clientId === clientId && client.clientSecret === clientSecret
+ })
+
+ var confidentialClients = config.confidentialClients.filter(function (client) {
+
+ return client.clientId === clientId && client.clientSecret === clientSecret
+ })
+
+ callback(false, clients[0] || confidentialClients[0])
+}
+
+var grantTypeAllowed = function (clientId, grantType, callback) {
+
+ var clientsSource,
+ clients = []
+
+ if (grantType === "password") {
+ clientsSource = config.clients
+ } else if (grantType === "client_credentials") {
+ clientsSource = config.confidentialClients
+ }
+
+ if (clientsSource) {
+ clients = clientsSource.filter(function (client) {
+
+ return client.clientId === clientId
+ })
+ }
+
+ callback(false, clients.length)
+}
+
+var saveAccessToken = function (accessToken, clientId, expires, user, callback) {
+
+ config.tokens.push({
+ accessToken: accessToken,
+ expires: expires,
+ clientId: clientId,
+ user: user
+ })
+
+ callback(false)
+}
+
+/*
+ * Method used only by password grant type.
+ */
+
+var getUser = function (username, password, callback) {
+
+ var users = config.users.filter(function (user) {
+
+ return user.username === username && user.password === password
+ })
+
+ callback(false, users[0])
+}
+
+/*
+ * Method used only by client_credentials grant type.
+ */
+
+var getUserFromClient = function (clientId, clientSecret, callback) {
+
+ var clients = config.confidentialClients.filter(function (client) {
+
+ return client.clientId === clientId && client.clientSecret === clientSecret
+ })
+
+ var user
+
+ if (clients.length) {
+ user = {
+ username: clientId
+ }
+ }
+
+ callback(false, user)
+}
+
+/**
+ * Export model definition object.
+ */
+
+module.exports = {
+ getAccessToken: getAccessToken,
+ getClient: getClient,
+ grantTypeAllowed: grantTypeAllowed,
+ saveAccessToken: saveAccessToken,
+ getUser: getUser,
+ getUserFromClient: getUserFromClient
+}
\ No newline at end of file
diff --git a/test/e2e-cypress/helpers/oauth2-server/swagger.yaml b/test/e2e-cypress/helpers/oauth2-server/swagger.yaml
new file mode 100644
index 00000000..314829f5
--- /dev/null
+++ b/test/e2e-cypress/helpers/oauth2-server/swagger.yaml
@@ -0,0 +1,36 @@
+swagger: "2.0"
+host: localhost:3231
+paths:
+ /password:
+ get:
+ summary: OAuth2 Password
+ security:
+ - oauthPassword: []
+ responses:
+ 200:
+ description: OK
+ schema:
+ type: string
+ /application:
+ get:
+ summary: OAuth2 Application
+ security:
+ - oauthApplication: []
+ responses:
+ 200:
+ description: OK
+ schema:
+ type: string
+securityDefinitions:
+ oauthPassword:
+ type: oauth2
+ flow: password
+ tokenUrl: /oauth/token
+ oauthApplication:
+ type: oauth2
+ flow: application
+ tokenUrl: /oauth/token
+ oauthImplicit:
+ type: oauth2
+ flow: implicit
+ authorizationUrl: /oauth/token
diff --git a/test/e2e-cypress/plugins/index.js b/test/e2e-cypress/plugins/index.js
index fd170fba..852cfa69 100644
--- a/test/e2e-cypress/plugins/index.js
+++ b/test/e2e-cypress/plugins/index.js
@@ -1,3 +1,4 @@
+const startOAuthServer = require("../helpers/oauth2-server")
// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
@@ -12,6 +13,7 @@
// the project's config changing)
module.exports = (on, config) => {
+ startOAuthServer()
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
}
diff --git a/test/e2e-cypress/support/index.js b/test/e2e-cypress/support/index.js
index a80764cb..83d39ac2 100644
--- a/test/e2e-cypress/support/index.js
+++ b/test/e2e-cypress/support/index.js
@@ -18,3 +18,10 @@ import "./commands"
// Alternatively you can use CommonJS syntax:
// require('./commands')
+
+
+// Remove fetch, so Cypress can intercept XHRs
+// see https://github.com/cypress-io/cypress/issues/95
+Cypress.on("window:before:load", win => {
+ win.fetch = null
+})
\ No newline at end of file
diff --git a/test/e2e-cypress/tests/deep-linking.js b/test/e2e-cypress/tests/features/deep-linking.js
similarity index 100%
rename from test/e2e-cypress/tests/deep-linking.js
rename to test/e2e-cypress/tests/features/deep-linking.js
diff --git a/test/e2e-cypress/tests/features/oauth2-flows/application.js b/test/e2e-cypress/tests/features/oauth2-flows/application.js
new file mode 100644
index 00000000..90446198
--- /dev/null
+++ b/test/e2e-cypress/tests/features/oauth2-flows/application.js
@@ -0,0 +1,55 @@
+describe("OAuth2 Application flow", function() {
+ beforeEach(() => {
+ cy.server()
+ cy.route({
+ url: "**/oauth/*",
+ method: "POST"
+ }).as("tokenRequest")
+ })
+
+ it("should make an application flow Authorization header request", () => {
+ cy
+ .visit("/?url=http://localhost:3231/swagger.yaml")
+ .get(".btn.authorize")
+ .click()
+
+ .get("div.modal-ux-content > div:nth-child(2)").within(() => {
+ cy.get("#client_id")
+ .clear()
+ .type("confidentialApplication")
+
+ .get("#client_secret")
+ .clear()
+ .type("topSecret")
+
+ .get("button.btn.modal-btn.auth.authorize.button")
+ .click()
+ })
+
+ cy.get("button.close-modal")
+ .click()
+
+ .get("#operations-default-get_application")
+ .click()
+
+ .get(".btn.try-out__btn")
+ .click()
+
+ .get(".btn.execute")
+ .click()
+
+ cy.get("@tokenRequest")
+ .its("request")
+ .its("body")
+ .should("equal", "grant_type=client_credentials")
+
+ cy.get("@tokenRequest")
+ .its("request")
+ .its("headers")
+ .its("authorization")
+ .should("equal", "Basic Y29uZmlkZW50aWFsQXBwbGljYXRpb246dG9wU2VjcmV0")
+
+ .get(".live-responses-table .response-col_status")
+ .contains("200")
+ })
+})
\ No newline at end of file
diff --git a/test/e2e-cypress/tests/features/oauth2-flows/password.js b/test/e2e-cypress/tests/features/oauth2-flows/password.js
new file mode 100644
index 00000000..872899a2
--- /dev/null
+++ b/test/e2e-cypress/tests/features/oauth2-flows/password.js
@@ -0,0 +1,122 @@
+describe("OAuth2 Password flow", function() {
+ beforeEach(() => {
+ cy.server()
+ cy.route({
+ url: "**/oauth/*",
+ method: "POST"
+ }).as("tokenRequest")
+ })
+
+ it("should make a password flow Authorization header request", () => {
+ cy
+ .visit("/?url=http://localhost:3231/swagger.yaml")
+ .get(".btn.authorize")
+ .click()
+
+ .get("#oauth_username")
+ .type("swagger")
+
+ .get("#oauth_password")
+ .type("password")
+
+ .get("#password_type")
+ .select("basic")
+
+ .get("#client_id")
+ .clear()
+ .type("application")
+
+ .get("#client_secret")
+ .clear()
+ .type("secret")
+
+ .get("div.modal-ux-content > div:nth-child(1) > div > div:nth-child(2) > div > div.auth-btn-wrapper > button.btn.modal-btn.auth.authorize.button")
+ .click()
+
+ .get("button.close-modal")
+ .click()
+
+ .get("#operations-default-get_password")
+ .click()
+
+ .get(".btn.try-out__btn")
+ .click()
+
+ .get(".btn.execute")
+ .click()
+
+ cy.get("@tokenRequest")
+ .its("request")
+ .its("body")
+ .should("include", "grant_type=password")
+ .should("include", "username=swagger")
+ .should("include", "password=password")
+ .should("not.include", "client_id")
+ .should("not.include", "client_secret")
+
+ cy.get("@tokenRequest")
+ .its("request")
+ .its("headers")
+ .its("authorization")
+ .should("equal", "Basic YXBwbGljYXRpb246c2VjcmV0")
+
+ .get(".live-responses-table .response-col_status")
+ .contains("200")
+ })
+
+ it("should make a Password flow request-body request", () => {
+ cy
+ .visit("/?url=http://localhost:3231/swagger.yaml")
+ .get(".btn.authorize")
+ .click()
+
+ .get("#oauth_username")
+ .type("swagger")
+
+ .get("#oauth_password")
+ .type("password")
+
+ .get("#password_type")
+ .select("request-body")
+
+ .get("#client_id")
+ .clear()
+ .type("application")
+
+ .get("#client_secret")
+ .clear()
+ .type("secret")
+
+ .get("div.modal-ux-content > div:nth-child(1) > div > div:nth-child(2) > div > div.auth-btn-wrapper > button.btn.modal-btn.auth.authorize.button")
+ .click()
+
+ .get("button.close-modal")
+ .click()
+
+ .get("#operations-default-get_password")
+ .click()
+
+ .get(".btn.try-out__btn")
+ .click()
+
+ .get(".btn.execute")
+ .click()
+
+ cy.get("@tokenRequest")
+ .its("request")
+ .its("body")
+ .should("include", "grant_type=password")
+ .should("include", "username=swagger")
+ .should("include", "password=password")
+ .should("include", "client_id=application")
+ .should("include", "client_secret=secret")
+
+ cy.get("@tokenRequest")
+ .its("request")
+ .its("headers")
+ .should("not.have.property", "authorization")
+
+ .get(".live-responses-table .response-col_status")
+ .contains("200")
+ })
+})
\ No newline at end of file