Skip to content

Instantly share code, notes, and snippets.

@RobinBoers
Last active July 10, 2023 20:38
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save RobinBoers/2fb550080bec7984bd68953b0fd0569d to your computer and use it in GitHub Desktop.
Save RobinBoers/2fb550080bec7984bd68953b0fd0569d to your computer and use it in GitHub Desktop.
Script to authenticate with the Somtoday API via SSO, by intercepting the redirect to the app.
{
"dependencies": {
"axios": "^1.4.0"
}
}
// NodeJS crap
// This unfortunately doesn't work in the browser atm :(
const crypto = require("crypto");
const axios = require('axios');
const { spawn } = require('node:child_process');
const readline = require("readline").createInterface({
input: process.stdin,
output: process.stdout,
});
// Configuration
//
// - Redirect URI should be a verified Somtoday callback URL. You intercept the code from this URL.
// - School UUID should be the UUID for your school. Can be found in the `organisations.json` file.
// - Third party method should be the `oidc_iss` shit from the login URL. It can also be found in the `organisations.json` file.
// - The client ID is a verified Somtoday client ID for OAuth. This one is from the Somtoday app.
const REDIRECT_URI = "somtodayouder://oauth/callback";
const SCHOOL_UUID = "ec284fda-3d0f-4f54-a77e-91d94b94ff1a";
const third_party_method = "https://accounts.google.com";
const CLIENT_ID = "CD4EBCF7-2809-4AB3-8D71-7D9CE518AF1B";
// Crypto utils
//
// Some utility functions for handling verifiers and such.
// Used in the OAuth code.
let url_encode = function (input) {
return input
.toString("base64")
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=/g, "");
};
let randomBytes = function (amountOfBytes) {
return crypto.randomBytes(amountOfBytes);
};
let sha256 = function (buffer) {
return crypto.createHash("sha256").update(buffer).digest();
};
let verifier = url_encode(randomBytes(32));
let challenge = url_encode(sha256(verifier));
// Main login flow
//
// We open a login window, in which the user authenticates using their credentials. Som will the redirect to the `REDIRECT_URI`,
// but since this is a fucking laptop it won't find the app, and error in the console, where you can then copy the code.
// The script will prompt for it, paste it there. It will then send a request to the Somtoday API to generate an access token.
function login() {
openLoginWindow();
askForInput();
}
function openLoginWindow() {
let url = `https://inloggen.somtoday.nl/oauth2/authorize?response_type=code&prompt=login&redirect_uri=${REDIRECT_URI}&client_id=${CLIENT_ID}&state=sompp&response_type=code&scope=openid&tenant_uuid=${SCHOOL_UUID}&session=no_session&code_challenge_method=S256&code_challenge=${challenge}`;
spawn("librewolf", [url]);
}
function askForInput() {
readline.question(
"Please enter code from Somtoday login callback: \n",
(code) => {
getAccessToken(code);
readline.close();
}
);
}
function getAccessToken(codeFromLoginWindow) {
let url = `https://inloggen.somtoday.nl/oauth2/token`;
let body = new URLSearchParams({
grant_type: "authorization_code",
redirect_uri: REDIRECT_URI,
code_verifier: verifier,
code: codeFromLoginWindow,
scope: "openid",
client_id: CLIENT_ID,
});
axios({
url: url,
method: "POST",
data: body,
}).then(function (response) {
console.log(response.data);
});
}
login();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment