Skip to content

Commit

Permalink
feat: fetch GitHub repositories of an authenticated principal
Browse files Browse the repository at this point in the history
  • Loading branch information
이은상 committed Dec 27, 2024
1 parent 14d3efd commit 8459c91
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 49 deletions.
8 changes: 4 additions & 4 deletions 7_auth/7_1_express_js/src/app.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import express from "express";
import cookieParser from "cookie-parser";
import { AuthController } from "./controllers/AuthController";
import {ProtectedController} from "./controllers/ProtectedController"; // Import the RedisController class
import {GitHubController} from "./controllers/GitHubController"; // Import the RedisController class

const app = express();
const PORT = 3000;

const authController = new AuthController();
const protectedController = new ProtectedController();
const protectedController = new GitHubController();

app.use(cookieParser());
app.use(express.json());
Expand All @@ -20,8 +20,8 @@ app.get("/callback", (req, res) =>
authController.callback(req, res)
)

app.get("/protected-remote-call", (req, res) =>
protectedController.getSomeResource(req, res)
app.get("/github/repositories", (req, res) =>
protectedController.getRepositories(req, res)
)

// Start the Express server
Expand Down
29 changes: 29 additions & 0 deletions 7_auth/7_1_express_js/src/controllers/GitHubController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Request, Response } from "express";
import { authenticate } from "../decorators/AuthDecorator";
import axios from "axios";

const GITHUB_API_URL = "https://api.github.com";

export class GitHubController {
@authenticate()
async getRepositories(req: Request, res: Response) {
try {
const user = req.user;

// Get user info from GitHub using the access token
const repoResponse = await axios.get(`${GITHUB_API_URL}/users/${user?.username}/repos`, {
headers: { Authorization: `Bearer ${user?.ghAccessToken}` },
});

if (user) {
res.send({
username: user.username,
repositories: repoResponse.data
});
}
} catch (error) {
console.error("Error in getUsername:", error);
res.status(500).send({ error: "Internal Server Error" });
}
}
}
20 changes: 0 additions & 20 deletions 7_auth/7_1_express_js/src/controllers/ProtectedController.ts

This file was deleted.

11 changes: 5 additions & 6 deletions 7_auth/7_1_express_js/src/decorators/AuthDecorator.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Request, Response, NextFunction } from "express";
import {NextFunction, Request, Response} from "express";
import jwt from "jsonwebtoken";
import Redis from "ioredis";

Expand Down Expand Up @@ -29,18 +29,17 @@ export function authenticate() {
}

try {
const decoded = jwt.verify(token, JWT_SECRET) as { username: string };
const principal = jwt.verify(token, JWT_SECRET) as { username: string, ghAccessToken: string };

// Validate token against Redis
const storedToken = await redis.get(
`user:${decoded.username}:app_user_token`
);
const storedToken = await redis.get(`user:${principal.username}:app_user_token`);
if (storedToken !== token) {
return res.status(401).send({ error: "Invalid token" });
}
principal.ghAccessToken = await redis.get(`user:${principal.username}:github_access_token`) as string;

// Attach user info to the request
req.user = decoded;
req.user = principal;
await originalMethod.call(this, req, res, next); // Call the original method
} catch (err) {
return res.status(403).send({ error: "Invalid or expired token" });
Expand Down
5 changes: 4 additions & 1 deletion 7_auth/7_1_express_js/src/types/express/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import { Request } from "express";
declare global {
namespace Express {
interface Request {
user?: { username: string };
user?: {
username: string
ghAccessToken: string
};
}
}
}
58 changes: 40 additions & 18 deletions 7_auth/client/src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,33 @@
import './App.css'
import './App.css';
import { useAuth } from "../Context.jsx";
import {useEffect, useState} from "react";
import { useEffect, useState } from "react";

function App() {

const { isAuthenticated, userInfo } = useAuth();
const [ remoteResource, setRemoteResource ] = useState(null);
const [repositories, setRepositories] = useState(null);

useEffect(() => {
if (isAuthenticated) {
const savedToken = localStorage.getItem("user_token");

if (savedToken) {
fetch("/api/protected-remote-call", {
fetch("/api/github/repositories", {
method: "GET",
headers: {
"Authorization": `Bearer ${savedToken}`
}
"Authorization": `Bearer ${savedToken}`,
},
})
.then(response => response.json())
.then(data => {
.then((response) => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.json();
})
.then((data) => {
console.log("Protected remote call response:", data);
setRemoteResource(data.resource);
setRepositories(data.repositories || []);
})
.catch(error => {
.catch((error) => {
console.error("Error during protected remote call:", error);
});
}
Expand All @@ -34,27 +38,45 @@ function App() {
<>
{isAuthenticated ? (
<>
<h2>Authenticated! Hello, {userInfo} 👋</h2>
{remoteResource && (
<div style={{ paddingTop: "25px" }}>
Here's the resource fetched from the remote server: <br/> {remoteResource}
<h2>Authenticated! Hello, {userInfo.username} 👋</h2>
{repositories && repositories.length > 0 ? (
<div style={{ paddingTop: "25px", textAlign: "left" }}>
<h4>Repositories ({repositories.length})</h4>
{repositories.map((repo) => (
<div key={repo.id} style={{ marginBottom: "8px" }}>
<a href={repo.html_url} target="_blank" rel="noopener noreferrer">
{repo.name}
</a>
</div>
))}
</div>
) : (
<p>No repositories found.</p>
)}
</>
) : (
<>
<h2>OAuth2</h2>
<div>
<button
onClick={() => { window.location.href = "/api/sign-in"; }}
style={{ margin: "5px" }}>
onClick={() => {
window.location.href = "/api/sign-in";
}}
style={{ margin: "5px" }}
>
GitHub
</button>
</div>
</>
)}
<div className="read-the-docs" style={{ paddingTop: "100px" }}>
<a href="https://github.com/CynicDog/Cloudnative-Node.js-Templates/tree/master/7_auth" target="_blank">GitHub Repo</a>
<a
href="https://github.com/CynicDog/Cloudnative-Node.js-Templates/tree/master/7_auth"
target="_blank"
rel="noopener noreferrer"
>
GitHub Repo
</a>
</div>
</>
);
Expand Down

0 comments on commit 8459c91

Please sign in to comment.