GitHub App e OAuth ~ Guida Pratica

Scritto da Francesco Di Donato • 26 aprile 2022 • 6 minuti di lettura
Cosa possiamo costruire
Un’interfaccia che dà a un utente autenticato su GitHub (o a qualsiasi organizzazione dell’utente) la possibilità di vedere quali dei suoi repository hanno un dato GitHub App installato.
Componente Import Git Repository di Vercel 🔗
Premessa
Ho trovato due modi diversi per raggiungere l’obiettivo.
- Fornitore autonomo (GitHub App)
- Flusso disgiunto (OAuth App, GitHub App).
Se conosci altre modalità , ti prego di contattarmi (twitter) 🔗.
In questo post implementiamo la prima modalità .
Crea un GitHub App
Per trovare l’elenco dei repository che hanno il GitHub App 🔗 installato, dobbiamo prima crearne uno 🔗.
Ai fini di questo post, dobbiamo solo sapere che per autenticare un utente tramite GitHub, devi registrare la tua OAuth app; tuttavia, ogni GitHub App ha un OAuth al suo interno 🔗.
Ecco perché chiamo (arbitrariamente) questo metodo autonomo - utilizziamo solo un singolo GitHub App.
- URL della homepage: http://localhost:3000 🔗
- URL di callback: Dove il fornitore deve rimandare l’utente una volta completato il flusso di autenticazione. Puoi scegliere qualsiasi percorso, sto usando /oauth/github/login/callback
- Setup URL: Dove il fornitore deve rimandare l’utente una volta che il GitHub è stato installato/disinstallato/cambiato il permesso. Puoi scegliere qualsiasi percorso, sto usando /new.
Configura l’app in modo che abbia il diritto di leggere i contenuti. Altrimenti, non potrà essere installata su repository specifici.
Troverai anche una sezione Webhook. Ai fini di questo post non ci interessa, quindi per continuare puoi semplicemente contrassegnarla come inattiva.
Infine crea il GitHub App. È stato assegnato un App ID, un Client ID ed è possibile generare un Client Secret - Tutte le informazioni di cui abbiamo bisogno, ma tra poco.
GitHub OAuth
Autenticare un utente con GitHub 🔗 è semplice.
Se qualcosa non è chiaro ti prego di fare riferimento al repo secondario 🔗.
Prima di tutto, il frontend presenta un CTA che punta a un percorso sul server.
<a id="oauth-github">Autenticati tramite GitHub</a>
<script>
const CLIENT_ID = "Iv1.395930440f268143";
const url = new URL("/login/oauth/authorize", "https://github.com");
url.searchParams.set("client_id", CLIENT_ID);
document.getElementById("oauth-github")
.setAttribute("href", url);
</script>
Nota: sicurezza Per rimanere conciso, riporto solo
client_id
poiché è richiesto; tuttavia, in un contesto di produzione assicurati di rivedere la documentazione ufficiale, specialmente riguardo ascope
estate
.
Rendilo carino, aggiungi l’icona di GitHub - l’utente preme e viene portato alla pagina seguente:
Ora, quando viene premuto il pulsante verde Autorizza
Assicuriamoci che il server sia in grado di ascoltare /oauth/github/login/callback
. Fai attenzione a questo passaggio fondamentale: GitHub reindirizza e aggiunge un parametro di query, un code
necessario per autenticare l’utente.
server.get("/oauth/github/login/callback", async (request, reply) => {
const { code } = request.query;
// ...
});
code
deve essere scambiato con un access_token
, che verrà memorizzato nel client e associato alle richieste all’API REST di GitHub.
Ora, prima di procedere, torna alla pagina di configurazione del tuo GitHub App e Genera un nuovo Client Secret. Quindi, usa dotenv
per questo.
const { code } = request.query;
const exchangeURL = new URL("login/oauth/access_token", "https://github.com");
exchangeURL.searchParams.set("client_id", process.env.CLIENT_ID);
exchangeURL.searchParams.set("client_secret", process.env.CLIENT_SECRET);
exchangeURL.searchParams.set("code", code);
const response = await axios.post(exchangeURL.toString(), null, {
headers: {
Accept: "application/json",
},
});
const { access_token } = response.data;
Per brevità , l’esempio non riporta la gestione degli errori. È lasciato al buon senso del lettore.
Quindi, il token viene consegnato al client che infine viene reindirizzato a qualche percorso /new
. Ma - ecco il nostro eroe della storia - riceve anche l’access_token
come parametro di query.
const { access_token } = response.data;
const redirectionURL = new URL("new", "http://localhost:3000");
redirectionURL.searchParams.set("access_token", access_token);
reply.status(302).header("Location", redirectionURL).send();
Estrailo nel client:
<script>
const access_token = new URL(window.location).searchParams.get(
"access_token"
);
localStorage.setItem("access_token", access_token);
</script>
Puoi memorizzarlo in qualsiasi Web Storage, in memoria, dipende davvero da come deve essere utilizzata la tua interfaccia. Anche se un cookie HttpOnly e Secure aiuterebbe a mitigare gli attacchi XSS.
Il client dell’utente autenticato può infine associare l’access_token
quando fa legittimamente una richiesta all’API REST di GitHub 🔗. Ci siamo quasi.
Miglioramento: popup invece di reindirizzamento Di solito altri OAuth in natura non reindirizzano duramente la pagina corrente. Supponiamo di lavorare su una SPA, pensa a quanto sia triste scoprire che questo flusso svuota il tuo store in memoria. Facile da risolvere, il reindirizzamento avverrà comunque ma in un popup creato (post) che, prima di dissolversi, passerà l’
access_token
di nuovo alla scheda principale.
Interrogare l’API REST di GitHub
I dati di cui abbiamo bisogno vengono restituiti da due endpoint.
Il secondo viene chiamato con le informazioni ricevute dal primo.
La documentazione afferma:
Devi utilizzare un token di accesso OAuth da utente a server, creato per un utente che ha autorizzato il tuo GitHub App, per accedere a questo endpoint.
L’access_token
restituito da una OAuth App “semplice” non è valido per questi endpoint. Tuttavia, c’è un modo per farlo funzionare usando un OAuth e tre altri endpoint diversi. Questo è mostrato nel secondo post.
const githubAPI = axios.create({
baseURL: "https://api.github.com",
headers: {
Accept: "application/vnd.github.v3+json",
},
});
const authorizationConf = {
headers: {
authorization: `token ${access_token}`,
},
};
(async () => {
const installationResponse = await githubAPI.get(
"user/installations",
authorizationConf
);
})();
Impostare
application/vnd.github.v3+json
è raccomandato dalla documentazione ufficiale. È il tipo di media personalizzato dell’API di GitHub.
Riceviamo indietro un elenco contenente… aspetta, è vuoto. Questo perché il GitHub App non è stato ancora installato da nessuna parte.
Facciamolo facile da installare:
<a
href="https://github.com/apps/<github-app-name>/installations/new"
>
Cambia permessi
</a>
Cliccandoci sopra mostra la seguente schermata:
Scegli il tuo account personale o uno delle tue organizzazioni. O entrambi. Installalo da qualche parte.
Ora /user/installations
restituisce un elenco di installazioni. Una per ogni account (account personale || organizzazione) che ha almeno un repo con il github app installato in esso.
Ogni elemento ha la proprietà id
. Questo è l’installation_id
necessario per il prossimo endpoint.
const promises = installationsResponse.data.installations.map(
(installation) => {
return githubAPI.get(
`user/installations/${installation.id}/repositories`,
authorizationConf
);
}
);
// parallel
const responses = await axios.all(promises);
const repositories = responses.map((response) => {
return response.data.repositories;
});
Ecco a te tutti i repositories
dell’utente autenticato o di una delle sue organizzazioni che hanno il GitHub App installato.
Epilogo
Per riassumere, ora puoi:
- Autenticare gli utenti tramite l’autenticazione OAuth di GitHub
- Reindirizzare gli utenti per installare il tuo GitHub App
- Mostrare loro un menu a discesa contenente tutte le organizzazioni oltre all’account personale
- Sapere quali repository mostrare
Lascio a te tutto il divertimento di implementare l’interfaccia utente con il tuo framework preferito. Se hai bisogno di guardare il tour completo, nel repo secondario 🔗 ho usato tecnologie che tutti conoscono.