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?
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