Thinkmill / keystatic

First class CMS experience, TypeScript API, Markdown & YAML/JSON based, no DB
https://keystatic.com
MIT License
1.13k stars 74 forks source link

Prevent login for people without write access github repo #831

Open CapitaineToinon opened 8 months ago

CapitaineToinon commented 8 months ago

Hey there,

I've been trying to setup Keystatic with a Github repo. It works great until I want new people to contribute. I understand that people need write access to use Keystatic but why allow people to login at all? Right now, people can login but get a cryptic error when they try to edit anything:

image

It seems like keystatic cloud handles this, if I try to login to https://keystatic.com/keystatic I get an expected error message:

image

Could we have something similar for the Github integration?

CapitaineToinon commented 8 months ago

As a work around, I added some code before loading the keystatic admin using a different access token to verify that the current keystatic logged in user has access to my repo. If not, shows an error. For this, I stopped using the astro integration and just imported the UI and the API endpoints to install them manually, like we used to do before the integration.

---
import { Keystatic } from '@/components/keystatic'
import { Octokit } from '@octokit/core'
import { z } from 'astro:content'
import Layout from '@/layouts/default.astro'
import { ExclamationTriangleIcon } from '@radix-ui/react-icons'
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
import config from 'keystatic.config'

export const prerender = false

const accessToken = Astro.cookies.get('keystatic-gh-access-token')
let error: { title: string; message: string } | undefined = undefined

if (config.storage.kind === 'github' && accessToken) {
    const souls = new Octokit({
        auth: import.meta.env.SOULS_PERSONAL_ACCESS_TOKEN,
    })

    const keystatic = new Octokit({
        auth: accessToken.value,
    })

    try {
        const me = await keystatic.request('GET /user', {
            headers: {
                'X-GitHub-Api-Version': '2022-11-28',
            },
        })

        await souls.request('GET /repos/{owner}/{repo}/collaborators/{username}', {
            owner: 'soulsspeedruns',
            repo: 'website',
            username: me.data.login,
            headers: {
                'X-GitHub-Api-Version': '2022-11-28',
            },
        })
    } catch (e) {
        const result = z.object({ status: z.literal(404) }).safeParse(e)

        if (result.success) {
            error = {
                title: 'Forbidden',
                message: `The user is not a verified contributor.`,
            }

            Astro.response.status = 403
        } else {
            console.log(e)
            throw e
        }
    }
}
---

{
    error ? (
        <Layout title="Unauthorized">
            <main class="container mx-auto mt-20 grid grid-cols-10">
                <div class="col-span-6 col-start-3">
                    <Alert
                        variant="destructive"
                        className="mb-6"
                    >
                        <ExclamationTriangleIcon className="h-4 w-4" />
                        <AlertTitle>{error.title}</AlertTitle>
                        <AlertDescription>{error.message}</AlertDescription>
                    </Alert>
                    <p class="prose max-w-none dark:prose-invert">
                        Please check <a href="/contribute">how to contribute</a>.
                    </p>
                </div>
            </main>
        </Layout>
    ) : (
        <Keystatic client:only="react" />
    )
}

I had to use a different access token because for some reason I just couldn't get the correct api information using the keystatic token. Not really sure why, not that familiar with github scopes and stuff. Works for now though.

AliMejbar commented 5 months ago

+1