-
Notifications
You must be signed in to change notification settings - Fork 71
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
20 changed files
with
1,271 additions
and
14 deletions.
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,234 @@ | ||
import { githubAuth } from "@hono/oauth-providers/github"; | ||
import { googleAuth } from "@hono/oauth-providers/google"; | ||
import { Hono } from "hono"; | ||
import { describeRoute } from "hono-openapi"; | ||
import { resolver } from "hono-openapi/zod"; | ||
import { authResponseSchema } from "./schema"; | ||
|
||
type Bindings = { | ||
GITHUB_CLIENT_ID: string; | ||
GITHUB_CLIENT_SECRET: string; | ||
GOOGLE_CLIENT_ID: string; | ||
GOOGLE_CLIENT_SECRET: string; | ||
AUTH_REDIRECT_URI: string; | ||
}; | ||
|
||
const app = new Hono<{ Bindings: Bindings }>(); | ||
|
||
app.use("/github", (c, next) => { | ||
githubAuth({ | ||
client_id: c.env.GITHUB_CLIENT_ID, | ||
client_secret: c.env.GITHUB_CLIENT_SECRET, | ||
}); | ||
|
||
return next(); | ||
}); | ||
|
||
app.get( | ||
"/github", | ||
describeRoute({ | ||
description: "Handle GitHub OAuth authentication", | ||
responses: { | ||
200: { | ||
description: "Successfully authenticated with GitHub", | ||
content: { | ||
"application/json": { | ||
schema: resolver(authResponseSchema), | ||
}, | ||
}, | ||
}, | ||
401: { | ||
description: "Authentication failed", | ||
content: { | ||
"application/json": { | ||
schema: resolver(authResponseSchema), | ||
}, | ||
}, | ||
}, | ||
}, | ||
}), | ||
async (c) => { | ||
const token = c.get("token"); | ||
const profile = c.get("user-github"); | ||
|
||
if (!token || !profile) { | ||
return c.json({ error: "Failed to authenticate with GitHub" }, 401); | ||
} | ||
|
||
return c.json({ | ||
data: { | ||
token, | ||
user: { | ||
email: profile.email, | ||
name: profile.name, | ||
provider: "github", | ||
}, | ||
}, | ||
}); | ||
}, | ||
); | ||
|
||
app.use("/google", (c, next) => { | ||
googleAuth({ | ||
client_id: c.env.GOOGLE_CLIENT_ID, | ||
client_secret: c.env.GOOGLE_CLIENT_SECRET, | ||
scope: ["openid", "email", "profile"], | ||
}); | ||
|
||
return next(); | ||
}); | ||
|
||
app.get( | ||
"/google", | ||
describeRoute({ | ||
description: "Handle Google OAuth authentication", | ||
responses: { | ||
200: { | ||
description: "Successfully authenticated with Google", | ||
content: { | ||
"application/json": { | ||
schema: resolver(authResponseSchema), | ||
}, | ||
}, | ||
}, | ||
401: { | ||
description: "Authentication failed", | ||
content: { | ||
"application/json": { | ||
schema: resolver(authResponseSchema), | ||
}, | ||
}, | ||
}, | ||
}, | ||
}), | ||
async (c) => { | ||
const token = c.get("token"); | ||
const profile = c.get("user-google"); | ||
|
||
if (!token || !profile) { | ||
return c.json({ error: "Failed to authenticate with Google" }, 401); | ||
} | ||
|
||
return c.json({ | ||
data: { | ||
token, | ||
user: { | ||
email: profile.email, | ||
name: profile.name, | ||
provider: "google", | ||
}, | ||
}, | ||
}); | ||
}, | ||
); | ||
|
||
app.post( | ||
"/token", | ||
describeRoute({ | ||
description: "Exchange OAuth token for user info", | ||
responses: { | ||
200: { | ||
description: "Successfully exchanged token", | ||
content: { | ||
"application/json": { | ||
schema: resolver(authResponseSchema), | ||
}, | ||
}, | ||
}, | ||
401: { | ||
description: "Invalid token", | ||
content: { | ||
"application/json": { | ||
schema: resolver(authResponseSchema), | ||
}, | ||
}, | ||
}, | ||
}, | ||
}), | ||
async (c) => { | ||
const token = c.req.header("Authorization")?.replace("Bearer ", ""); | ||
|
||
if (!token) { | ||
return c.json({ error: "No token provided" }, 401); | ||
} | ||
|
||
try { | ||
// TODO: Implement token verification and user info retrieval | ||
// This would involve: | ||
// 1. Verifying the token's validity | ||
// 2. Fetching associated user information | ||
// 3. Returning the user data in the same format as the OAuth endpoints | ||
|
||
return c.json({ | ||
data: { | ||
token, | ||
user: { | ||
// User data would be populated from token verification | ||
email: "", | ||
name: "", | ||
provider: "github", // or "google" depending on token | ||
}, | ||
}, | ||
}); | ||
} catch (error) { | ||
return c.json({ error: "Invalid token" }, 401); | ||
} | ||
}, | ||
); | ||
|
||
app.post( | ||
"/revalidate", | ||
describeRoute({ | ||
description: "Revalidate an existing auth token", | ||
responses: { | ||
200: { | ||
description: "Successfully revalidated token", | ||
content: { | ||
"application/json": { | ||
schema: resolver(authResponseSchema), | ||
}, | ||
}, | ||
}, | ||
401: { | ||
description: "Invalid or expired token", | ||
content: { | ||
"application/json": { | ||
schema: resolver(authResponseSchema), | ||
}, | ||
}, | ||
}, | ||
}, | ||
}), | ||
async (c) => { | ||
const token = c.req.header("Authorization")?.replace("Bearer ", ""); | ||
|
||
if (!token) { | ||
return c.json({ error: "No token provided" }, 401); | ||
} | ||
|
||
try { | ||
// TODO: Implement token revalidation logic | ||
// This would involve: | ||
// 1. Verifying the token hasn't expired | ||
// 2. Checking if the token is still valid | ||
// 3. Optionally refreshing the token if needed | ||
// 4. Returning updated token and user info | ||
|
||
return c.json({ | ||
data: { | ||
token, | ||
user: { | ||
// User data would be populated from token verification | ||
email: "", | ||
name: "", | ||
provider: "github", // or "google" depending on token | ||
}, | ||
}, | ||
}); | ||
} catch (error) { | ||
return c.json({ error: "Invalid or expired token" }, 401); | ||
} | ||
}, | ||
); | ||
|
||
export default app; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import z from "zod"; | ||
import "zod-openapi/extend"; | ||
|
||
export const authResponseSchema = z | ||
.object({ | ||
data: z | ||
.object({ | ||
token: z.string().openapi({ | ||
example: "jwt_token", | ||
description: "JWT authentication token", | ||
}), | ||
user: z | ||
.object({ | ||
email: z.string().email().openapi({ | ||
example: "[email protected]", | ||
description: "User's email address", | ||
}), | ||
name: z.string().openapi({ | ||
example: "John Doe", | ||
description: "User's full name", | ||
}), | ||
provider: z.enum(["github", "google"]).openapi({ | ||
example: "github", | ||
description: "OAuth provider used for authentication", | ||
}), | ||
}) | ||
.openapi({ description: "User profile information" }), | ||
}) | ||
.optional() | ||
.openapi({ description: "Response data" }), | ||
error: z.string().optional().openapi({ | ||
example: "Authentication failed", | ||
description: "Error message if request failed", | ||
}), | ||
}) | ||
.openapi({ ref: "AuthResponse" }); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { Hono } from "hono"; | ||
import { describeRoute } from "hono-openapi"; | ||
import { resolver, validator as zValidator } from "hono-openapi/zod"; | ||
import { bodySchema, responseSchema } from "./schema"; | ||
|
||
const app = new Hono(); | ||
|
||
app.post( | ||
"/", | ||
describeRoute({ | ||
description: "Submit feedback", | ||
responses: { | ||
200: { | ||
description: "Successful feedback submission", | ||
content: { | ||
"application/json": { | ||
schema: resolver(responseSchema), | ||
}, | ||
}, | ||
}, | ||
}, | ||
}), | ||
zValidator("json", bodySchema), | ||
(c) => { | ||
return c.json({ data: "Feedback received!" }); | ||
}, | ||
); | ||
|
||
export default app; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import z from "zod"; | ||
import "zod-openapi/extend"; | ||
|
||
export const bodySchema = z | ||
.object({ | ||
source: z | ||
.string() | ||
.openapi({ example: "en", description: "Source language code" }), | ||
target: z | ||
.string() | ||
.openapi({ example: "es", description: "Target language code" }), | ||
input: z | ||
.string() | ||
.openapi({ example: "Hello world", description: "Original text" }), | ||
translation: z | ||
.string() | ||
.openapi({ example: "Hola mundo", description: "Machine translation" }), | ||
suggestion: z | ||
.string() | ||
.optional() | ||
.openapi({ example: "¡Hola mundo!", description: "User suggestion" }), | ||
comment: z | ||
.string() | ||
.optional() | ||
.openapi({ example: "Too formal", description: "Additional comments" }), | ||
}) | ||
.openapi({ ref: "Body" }); | ||
|
||
export const responseSchema = z | ||
.string() | ||
.openapi({ example: "Feedback received!" }); |
Oops, something went wrong.