Skip to content

Instantly share code, notes, and snippets.

@Terrance
Last active June 26, 2018 12:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Terrance/e0b2a0ebbff3c9ca98a6f9a405ccf835 to your computer and use it in GitHub Desktop.
Save Terrance/e0b2a0ebbff3c9ca98a6f9a405ccf835 to your computer and use it in GitHub Desktop.
Alternative single-HTML-file Monzo login page, a drop-in replacement for https://auth.monzo.com.
<!DOCTYPE html>
<html>
<head>
<title>Monzo account access</title>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.1/css/bulma.min.css"
integrity="sha256-zIG416V1ynj3Wgju/scU80KAEWOsO5rRLfVyRDuOv7Q="
crossorigin="anonymous">
<style>
html {
overflow-y: auto;
}
body {
background-color: #ff3860;
}
#fixed {
max-width: 768px;
margin-left: auto;
margin-right: auto;
padding: 0;
overflow: hidden;
}
.section {
padding: 1.5rem;
}
.content:not(:last-child) {
margin-bottom: 0;
}
.upper {
background-color: #363636;
}
.upper .title {
color: #f5f5f5;
}
.lower {
background-color: #f5f5f5;
}
#error {
background-color: #ffdd57;
}
.raw {
margin-top: 1.5rem;
}
#error, #info, #login, #done, .raw:empty {
display: none;
}
</style>
</head>
<body>
<section class="hero is-fullheight">
<div class="hero-body">
<div class="container">
<div id="fixed" class="box">
<div class="section upper">
<h1 class="title">Monzo account access</h1>
</div>
<div id="loading" class="section lower">
<p>Just a moment...</p>
</div>
<div id="info" class="section content">
<h2 id="info-name" class="subtitle"></h2>
<blockquote id="info-description"></blockquote>
<p>This app will be able to:</p>
<ul id="info-scope-descriptions"></ul>
<pre id="info-raw" class="raw"></pre>
</div>
<div id="error" class="section content">
<div id="error-list"></div>
<pre id="error-raw" class="raw"></pre>
</div>
<div id="login" class="section lower">
<form class="columns">
<div class="column is-8">
<input id="login-email" type="email" class="input" placeholder="Email address" required>
</div>
<div class="column">
<button id="login-submit" type="submit" class="button is-fullwidth is-danger">Authorise</button>
</div>
</form>
<p id="login-redirect">After confirming the login via email, you will be redirected to <span id="login-domain"></span>.</p>
</div>
<div id="done" class="section lower">
<p>Check your email at <span id="done-email"></span> to continue. You can now close this page.</p>
</div>
</div>
</div>
</div>
</section>
<script
src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous"></script>
<script>
"use strict";
$(document).ready(function() {
function query() {
let data = {};
for (var kv of location.search.substring(1).split("&")) {
let split = kv.split("=");
let k = decodeURIComponent(split.shift());
let v = decodeURIComponent(split.join("="));
data[k] = v;
}
if (!("raw" in data)) {
$(".raw").hide();
}
return data;
}
var err_strings = {
missing: {
"response_type": "<code>response_type</code> must be set to <code>\"code\"</code>.",
"client_id": "<code>client_id</code> must be specified.",
"redirect_uri": "<code>redirect_uri</code> must be specified."
},
invalid: {
"redirect_uri": "<code>redirect_uri</code> differs between the client and the request."
},
lookup: {
"not_found.client": "No application exists for the given client ID.",
"internal_service": "An unknown error occurred whilst submitting your request."
},
auth: {
"bad_request.client_not_found": "No application exists for the given client ID.",
"bad_request.bad_param.redirect_uri": "<code>redirect_uri</code> isn't specified, or differs between the client and the request.",
"bad_request.bad_param.recipient_email": "The email address you entered doesn't belong to a Monzo account.",
"bad_request.client_not_published": "The application you're authorising is not public, and you're not whitelisted as a collaborator.",
"internal_service": "An unknown error occurred whilst submitting your request."
}
};
function lookup(query, success, failed) {
let errors = [];
if (query.response_type !== "code") {
errors.push(err_strings.missing.response_type);
}
if (!query.client_id) {
errors.push(err_strings.missing.client_id);
}
if (!query.redirect_uri) {
errors.push(err_strings.missing.redirect_uri);
}
if (errors.length) {
failed(errors);
return;
}
$.ajax({
url: "https://api.monzo.com/oauth2/clients/" + encodeURIComponent(query.client_id),
statusCode: {
200: function(data) {
if (data.redirect_uri !== query.redirect_uri) {
failed([err_strings.invalid.redirect_uri,
"Client: <code>" + data.redirect_uri + "</code><br>Request: <code>" + query.redirect_uri + "</code>"], data);
} else {
success(data);
}
},
404: function(data) {
failed([err_strings.lookup[data.responseJSON.code] || data.responseJSON.message], data.responseJSON);
},
500: function(data) {
failed([err_strings.lookup[data.responseJSON.code] || data.responseJSON.message], data.responseJSON);
}
},
error: function(jqXHR, status, error) {
if (status !== "error" && error !== "Not Found") {
failed([err_strings.lookup.internal_service]);
}
}
});
}
function auth(query, email, success, failed) {
$("#error").hide();
$("#login-email").prop("disabled", true);
$("#login-submit").addClass("is-loading");
$.ajax({
method: "post",
url: "https://api.monzo.com/oauth2/authorize",
data: {
client_id: query.client_id,
email: email,
redirect_uri: query.redirect_uri,
response_type: query.response_type,
state: query.state || ""
},
statusCode: {
200: function(data) {
success(email);
},
400: function(data) {
$("#login-email").prop("disabled", false);
$("#login-submit").removeClass("is-loading");
failed([err_strings.auth[data.responseJSON.code] || data.responseJSON.message], data.responseJSON);
},
500: function(data) {
$("#login-email").prop("disabled", false);
$("#login-submit").removeClass("is-loading");
failed([err_strings.auth[data.responseJSON.code] || data.responseJSON.message], data.responseJSON);
}
},
error: function(jqXHR, status, error) {
if (status !== "error" && error !== "Bad Request") {
failed([err_strings.lookup.internal_service]);
}
}
});
}
function error(errors, data) {
$("#error-list").empty();
for (var e of errors) {
$("#error-list").append($("<p>").html(e));
}
if (data) {
$("#error-raw").text(JSON.stringify(data, null, 2));
}
$("#loading").hide();
$("#error").show();
}
function info(data) {
$("#info-name").text(data.name);
$("#info-description").text(data.description);
for (var s of (data.scope_descriptions || []).concat().sort()) {
$("#info-scope-descriptions").append($("<li>").text(s));
}
$("#info-raw").text(JSON.stringify(data, null, 2));
try {
$("#login-domain").text(new URL(data.redirect_uri).host);
} catch (e) {
$("#login-redirect").hide();
}
$("#loading").hide();
$("#info, #login").show();
$("#login-email").focus();
}
function done(email) {
$("#done-email").text(email);
$("#login").hide();
$("#done").show();
}
var data = query();
lookup(data, info, error);
$("#login form").submit(function() {
auth(data, $("#login-email").val(), done, error);
return false;
});
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment