cypresscypress-origin

cy.origin() and immediate redirect


I am having trouble with testing my oauth-secured application. The problem manifests itself when there is no public page - user is immediately redirected to OAuth server it they are not authenticated.

I managed to reproduce the problem in much simpler setup:

Here are respective apps (in Flask):

Fake app

from flask import Flask, redirect, render_template_string

app = Flask(__name__)

app_host="fake-app"
app_port=5000
app_uri=f"http://{app_host}:{app_port}"
oauth_host="fake-oauth-server"
oauth_port=5001
oauth_uri=f"http://{oauth_host}:{oauth_port}"

@app.route('/')
def hello():
    return render_template_string('''<!doctype html>
           <html>
               <body>
                   <p>Hello, World MainApp!</p>
                   <a id="loginButton" href="{{ oauth_uri }}?redirect_uri={{ app_uri }}">Login</a>
               </body>
           </html>
           ''',
           oauth_uri=oauth_uri,
           app_uri=app_uri
    )

@app.route('/goto-oauth')
def goto_oauth():
    return redirect(f"{oauth_uri}?redirect_uri={app_uri}")

if __name__ == '__main__':
    app.run(host=app_host, port=app_port)

Fake oauth server:

from flask import Flask, render_template_string, request

app = Flask(__name__)

oauth_host="fake-oauth-server"
oauth_port=5001

@app.route('/')
def login():
    return render_template_string(
    '''<!doctype html>
      <html>
          <body>
              <p>Please log in</p>
              <label>Username: <label><input id="username" />
              <label>Password: <label><input id="password" />
              <a id="submit-password" href="{{ redirect_uri }}">Submit</a>
          </body>
      </html>
      ''', redirect_uri=request.args.get('redirect_uri'))


if __name__ == '__main__':
    app.run(host=oauth_host, port=oauth_port)

First flow: there is a publicly available page with Login button

This is possible to test with cy.origin:

describe('My Scenarios', () => {
  beforeEach(() => {
    cy.visit('/');
    cy.contains('MainApp');
    cy.get('a#loginButton').click();
    cy.origin('http://fake-oauth-server:5001', () => {
      cy.contains('Please log in');
      cy.get('input#username').type('user1');
      cy.get('input#password').type('password1');
      cy.get('a#submit-password').click()
    });
  });

  it.only('test flask', () => {
    cy.visit('/');
    cy.contains('MainApp');
  });
});

Problematic flow: immediate redirect to Oauth server

describe('My Scenarios', () => {
  beforeEach(() => {
    cy.visit('/goto-oauth');

    cy.origin('http://fake-oauth-server:5001', () => {
      cy.contains('Please log in');
      cy.get('input#username').type('user1');
      cy.get('input#password').type('password1');
      cy.get('a#submit-password').click()
    });
  });

  it.only('test flask', () => {
    cy.visit('/');
    cy.contains('MainApp');
  });
});

Fails with:

CypressError: `cy.origin()` requires the first argument to be a different domain than top. You passed `http://fake-oauth-server:5001` to the origin command, while top is at `http://fake-oauth-server:5001`.

Either the intended page was not visited prior to running the cy.origin block or the cy.origin block may not be needed at all.

There is no publicly available page in my app - how can I amend the test to make it work?


Solution

  • It seems to work if visit the redirect URL inside the cy.origin().

    I set the app on http://localhost:6001 and the auth server on http://localhost:6003, using express rather than flask.

    Test

    describe('My Scenarios', () => {
      beforeEach(() => {
        cy.origin('http://localhost:6003', () => {
          cy.visit('http://localhost:6001/goto-oauth')
          cy.contains('Please log in');
          cy.get('input#username').type('user1');
          cy.get('input#password').type('password1');
          cy.get('a#submit-password').click()
        });
      });
    
      it('test main app', () => {
        cy.visit('http://localhost:6001')
        cy.contains('MainApp')
      })
    })
    

    App

    const express = require('express')
    function makeApp() {
      const app = express()
      app.get('/', function (req, res) {
        res.send(`
          <html>
          <body>
            <p>Hello, World MainApp!</p>
            <a id="loginButton" href="http://localhost:6003?redirect_uri=http://localhost:6001">
              Login
            </a>
          </body>
          
          </html>
        `)
      })
      app.get('/goto-oauth', function (req, res) {
        res.redirect('http://localhost:6003')
      })
    
      const port = 6001
    
      return new Promise((resolve) => {
        const server = app.listen(port, function () {
          const port = server.address().port
          console.log('Example app listening at port %d', port)
    
          // close the server
          const close = () => {
            return new Promise((resolve) => {
              console.log('closing server')
              server.close(resolve)
            })
          }
    
          resolve({ server, port, close })
        })
      })
    }
    
    module.exports = makeApp
    

    Auth

    const express = require('express')
    function makeServer() {
      const app = express()
      app.get('/', function (req, res) {
        res.send(`
        <!doctype html>
        <html>
            <body>
                <p>Please log in</p>
                <label>Username: <label><input id="username" />
                <label>Password: <label><input id="password" />
                <a id="submit-password" href="http://localhost:6001">Submit</a>
            </body>
        </html>
        `)
      })
    
      const port = 6003
    
      return new Promise((resolve) => {
        const server = app.listen(port, function () {
          const port = server.address().port
          console.log('Example app listening at port %d', port)
    
          // close the server
          const close = () => {
            return new Promise((resolve) => {
              console.log('closing server')
              server.close(resolve)
            })
          }
    
          resolve({ server, port, close })
        })
      })
    }
    
    module.exports = makeServer