Skip to content
This repository was archived by the owner on Oct 31, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 34 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,37 @@ Once you've installed dependencies, start a development server:
```bash
npm run dev
```
> You can preview the built app with `npm run preview`, regardless of whether you installed an adapter. Note that I don't use this in production, I build with adapter-vercel
> You can preview the built app with `npm run preview`.

# ScratchLight
This repository also includes the code for the ScratchLight authentication system, which Scratchinfo uses.

To use ScratchLight, just send your user to `/scratchlight/authpage?redirect=` with the `redirect` URL parameter being the Base64 encoded version of the page you want to direct your user to after they have authenticated, **excluding** the protocol and `://` which is assumed to be `https://`.

You also will need to add the `username` URL parameter with the value being the username of the user you would like to authenticate.

Then the user will be returned to your site with a `verifyCode` URL parameter.

Then once your user has returned to your site, `POST` to `/scratchlight/verify` with a request like this:
```json
{
"code": "Your verifyCode URL parameter"
}
```
ScratchLight will either return two responses: one for success, and one for failure. I recommend verifying the authentication status on the server, so that you can issue a JWT/session cookie.

Success:
```json
{
"isError": false,
"username": "god286"
}
```

Failure:
```json
{
"isError": true
}
```
**After you have requested the `verifyCode` of your user, their authentication will be removed from our database.**
18 changes: 16 additions & 2 deletions src/lib/bta.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Thanks to https://gist.github.com/oeon/0ada0457194ebf70ec2428900ba76255 for the code!
const b2a = (a) => {
// Part of the code has been modified for TypeScript.
const b2a = (a): string => {
var c,
d,
e,
Expand Down Expand Up @@ -32,4 +33,17 @@ const b2a = (a) => {
(o ? m.slice(0, o - 3) : m) + "===".slice(o || 3)
);
};
export default b2a;
function a2b(a): string {
var b, c, d, e = {}, f = 0, g = 0, h = "", i = String.fromCharCode, j = a.length;
for (b = 0; 64 > b; b++) e["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(b)] = b;
for (c = 0; j > c; c++) for (b = e[a.charAt(c)], f = (f << 6) + b, g += 6; g >= 8; ) ((d = 255 & f >>> (g -= 8)) || j - 2 > c) && (h += i(d));
return h;
};
function multi(decode, value): string {
if (decode) {
return a2b(value)
} else {
return b2a(value)
}
}
export default multi;
2 changes: 2 additions & 0 deletions src/routes/__error.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<h1>Ah, oh no!</h1>
<p>An error has occurred. This page might not exist or may have moved.</p>
2 changes: 1 addition & 1 deletion src/routes/dashboard.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
}
};
// CUD = current user data
const CUDFetch = await fetch("/you/supa/fsauth/getUserFromToken", {
const CUDFetch = await fetch("/you/supa/scratchlight/getUserToken", {
method: "POST",
body: JSON.stringify({
token: window.localStorage.getItem("authToken").toString(),
Expand Down
37 changes: 23 additions & 14 deletions src/routes/login.svelte
Original file line number Diff line number Diff line change
@@ -1,25 +1,34 @@
<script lang="ts">
import { onMount } from "svelte";
import b2a from "$lib/bta";
import multi from "$lib/bta";
let submitFunction: Function = function () {};
let isLoggedIn = false;
let isLoggedIn = false;
let username = "";
onMount(() => {
isLoggedIn = !!window.localStorage.getItem("authToken")
if (isLoggedIn) {
window.location.href = "/"
}
isLoggedIn = !!window.localStorage.getItem("authToken");
if (isLoggedIn) {
window.location.href = "/";
}
submitFunction = function () {
console.log("Sending user to FluffyScratch for authentication...");
const urlEncode = b2a(`${window.location.host}/you/supa/fsauth/checkAuth`);
window.location.href = `https://fluffyscratch.hampton.pw/auth/getKeys/v2?redirect=${urlEncode}`;
const urlEncode = multi(
false,
`${window.location.host}/you/supa/scratchlight/verify`
);
window.location.href = `/scratchlight/authpage?redirect=${urlEncode}&username=${username}`;
};
});
</script>

<h1>Log in with FluffyScratch Auth</h1>
<hr>
<button type="button" on:click={submitFunction()} class="btn btn-primary">Log in with FluffyScratch</button>
<br> <br>
<a href="/privacy">Privacy Policy</a>
<h1>Log in with ScratchLight Auth</h1>
<hr />
<label for="usernameInput">Enter in your username here:</label>
<br>
<input id="usernameInput" name="usernameInput" type="text" bind:value={username}>
<br>
<p style="font-size: 0.75em">Your username will automatically be retrieved when you authenticate with FluffyScratch.</p>
<br>
<button type="button" on:click={submitFunction()} class="btn btn-primary"
>Log in with ScratchLight</button
>
<br /> <br />
<a href="/privacy">Privacy Policy</a>
9 changes: 3 additions & 6 deletions src/routes/privacy.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!-- Change this date when you modify the policy -->
<i>Last modified: 15 November 2021</i>
<i>Last modified: 17 November 2021</i>
<br />
<p>
All Scratchinfo pages are hosted through <a href="https://vercel.com"
Expand All @@ -24,18 +24,15 @@
</p>
<hr />
<p>
When you are using Scratchinfo You, your Scratch account is verified by the <a
href="https://github.com/hamptonmoore/FluffyScratch"
>FluffyScratch authentication platform</a
>. FluffyScratch allows Scratchinfo to verify your Scratch account.
When you are using Scratchinfo You, your Scratch account is verified by ScratchLight which is made by ScratchInfo. Anyone can see your ScratchInfo You page.
</p>
<p>
Our PostgreSQL database is provided by <a href="https://supabase.io"
>Supabase</a
>. All of the data used to provide Scratchinfo You is on Supabase.
</p>
<p>
Scratchinfo is open source on <a
Scratchinfo and ScratchLight are open source on <a
href="https://github.com/webdev03/scratchinfo">our GitHub repository</a
> and is licensed under the MIT License.
</p>
34 changes: 34 additions & 0 deletions src/routes/scratchlight/__layout.reset.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<svelte:head>
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.2/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-uWxY/CJNBR+1zjPWmfnSnVxwRheevXITnMqoEIeG1LJrdI0GlVs/9cVSyPYXdcSF"
crossorigin="anonymous"
/>
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.2/dist/js/bootstrap.bundle.min.js"
integrity="sha384-kQtW33rZJAHjgefvhyyzcGF3C5TFyBQBA13V1RKPf4uH+bwyzQxZ6CmMZHmNBEfJ"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.6.0/font/bootstrap-icons.css"
/>
<title>ScratchLight Authentication</title>
</svelte:head>
<div class="fp">
<div class="container">
<div class="pt-3">
<h1>ScratchLight Authentication</h1>
<slot />
</div>
</div>
</div>
<style>
.fp {
min-width: 100vw;
min-height: 100vh;
background-color: #030d1b;
color: white;
}
</style>
73 changes: 73 additions & 0 deletions src/routes/scratchlight/authpage.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<script lang="ts">
// Hi there! Read the README.md of this repository to learn more about
// the ScratchLight Authentication service.
import { onMount } from "svelte";
import multi from "$lib/bta";
let code = "Loading...";
let problem,
authIncorrect = false;
let loading = true;
let copyFunction: Function = function () {};
let redirectLink = "/";
onMount(async () => {
const urlParams = new URLSearchParams(window.location.search);
console.log(urlParams)
if (!(urlParams.has("username") || urlParams.has("redirect"))) {
authIncorrect = true;
throw new Error("The webmaster hasn't set up ScratchLight correctly.")
}
const createAuthReq = await fetch("/scratchlight/createAuthSession", {
method: "POST",
body: JSON.stringify({
username: urlParams.get("username"),
}),
});
loading = false;
if (createAuthReq.ok) {
problem = false;
const authJSON = await createAuthReq.json();
code = authJSON.code;
redirectLink = "https://" + multi(true, urlParams.get("redirect")).toString() + "?privateCode=" + authJSON.private.toString();
} else {
problem = true;
}
copyFunction = function () {
navigator.clipboard.writeText(code);
};
});
</script>

{#if authIncorrect}
<div class="alert alert-danger" role="alert">
The website administrator hasn't set up ScratchLight correctly, so ScratchLight won't work. If you're the website owner, please read the README of the scratchinfo repository on GitHub.
</div>
{/if}
{#if problem}
<div class="alert alert-danger" role="alert">
An error has occurred.
</div>
{/if}
<h3>
Please comment this code on the ScratchLight Authentication project, then
press on the "I'm Done" button.
</h3>
<p><b>Only include the code. No spaces or any characters other than the code will work.</b></p>
<div id="code" class="card">
<div class="card-body">{code}</div>
</div>
<button class="btn btn-primary" on:click={copyFunction()}>Copy code</button>
<br /> <br />
<a class="btn btn-primary" target="_blank" href="https://scratch.mit.edu/projects/601968190/"
>Go to Project</a
>
<a class="btn btn-primary" href={redirectLink}>I'm Done</a>

<style>
.card {
color: black;
}
.card {
width: 100%;
align-items: center;
}
</style>
56 changes: 56 additions & 0 deletions src/routes/scratchlight/createAuthSession.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/**
* @type {import('@sveltejs/kit').RequestHandler}
*/
import { createClient } from "@supabase/supabase-js";
import crypto from "crypto"
import dotenv from "dotenv";
dotenv.config();

export async function post(request) {
try {
let parsedBody: any;
try {
parsedBody = JSON.parse(request.body);
} catch {
parsedBody = request.body;
}
if (typeof parsedBody.username == "undefined") {
return {
status: 500,
body: {
isError: true,
msg: "Please add a username in your POST request."
}
}
}
let postCode = crypto.randomBytes(128).toString('hex');
postCode = postCode.replace(/[0-9]/g, '');
const privateCode = crypto.randomBytes(24).toString('hex');
const supabase = createClient(
process.env["SCRATCHLIGHT_URL"],
process.env["SCRATCHLIGHT_KEY"]
)
const createSession = await supabase
.from("codes")
.insert([{ code: postCode, user: parsedBody.username, privateCode: privateCode }]);
if (createSession.error) {
throw new Error("Oh noes! An error has occurred!")
}
return {
body: {
code: postCode,
username: parsedBody.username,
private: privateCode
}
}
} catch (err) {
console.error(err);
return {
status: 500,
body: {
isError: true,
msg: "An error has occurred."
}
}
}
}
63 changes: 63 additions & 0 deletions src/routes/scratchlight/verify.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/**
* @type {import('@sveltejs/kit').RequestHandler}
*/
import { createClient } from "@supabase/supabase-js";
import dotenv from "dotenv";
dotenv.config();

export async function post(request) {
try {
let parsedBody: any;
try {
parsedBody = JSON.parse(request.body);
} catch {
parsedBody = request.body;
}
const privateCode = parsedBody.privateCode;
if (typeof privateCode == "undefined") {
throw new Error("no private code")
}
const supabase = createClient(
process.env["SCRATCHLIGHT_URL"],
process.env["SCRATCHLIGHT_KEY"]
)
const getData = await supabase
.from('codes')
.select()
.eq("privateCode", privateCode)
if (getData.error || getData.data.length == 0) {
throw new Error("Cannot find session.")
}
const comments = await (await fetch("https://api.scratch.mit.edu/users/god286/projects/601968190/comments/?limit=15&offset=0")).json();
for (let index = 0; index < comments.length; index++) {
const comment = comments[index];
if (comment.content == getData.data[0]["code"] && comment.author.username.toLowerCase() == getData.data[0]["user"].toLowerCase()) {
const deleteAuthSession = await supabase
.from('codes')
.delete()
.eq("privateCode", privateCode);
return {
body: {
isError: false,
username: getData.data[0]["user"]
}
}
}
}
return {
status: 500,
body: {
isError: true,
msg: "We couldn't verify this auth session."
}
}
} catch {
return {
status: 500,
body: {
isError: true,
msg: "An error has occurred."
}
}
}
}
Loading