Skip to content

Commit

Permalink
Token refreshing
Browse files Browse the repository at this point in the history
  • Loading branch information
tom-sherman committed Sep 7, 2024
1 parent 373587c commit 14b31d7
Show file tree
Hide file tree
Showing 20 changed files with 420 additions and 292 deletions.
3 changes: 2 additions & 1 deletion packages/frontpage/app/(app)/_components/post-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import { ensureUser, getUser } from "@/lib/data/user";
import { TimeAgo } from "@/lib/components/time-ago";
import { VoteButton } from "./vote-button";
import { PostCollection } from "@/lib/data/atproto/post";
import { DID, getVerifiedHandle } from "@/lib/data/atproto/did";
import { getVerifiedHandle } from "@/lib/data/atproto/identity";
import { UserHoverCard } from "@/lib/components/user-hover-card";
import type { DID } from "@/lib/data/atproto/did";

type PostProps = {
id: number;
Expand Down
2 changes: 1 addition & 1 deletion packages/frontpage/app/(app)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Button } from "@/lib/components/ui/button";
import { isBetaUser } from "@/lib/data/user";
import { OpenInNewWindowIcon } from "@radix-ui/react-icons";
import { ThemeToggle } from "./_components/theme-toggle";
import { getDidFromHandleOrDid } from "@/lib/data/atproto/did";
import { getDidFromHandleOrDid } from "@/lib/data/atproto/identity";

import {
DropdownMenu,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import "server-only";
import { getDidFromHandleOrDid } from "@/lib/data/atproto/did";
import { getDidFromHandleOrDid } from "@/lib/data/atproto/identity";
import {
getCommentWithChildren,
shouldHideComment,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Comment } from "../../_lib/comment";
import Link from "next/link";
import { Metadata } from "next";
import { getVerifiedHandle } from "@/lib/data/atproto/did";
import { getVerifiedHandle } from "@/lib/data/atproto/identity";
import { CommentPageParams, getCommentPageData } from "./_lib/page-data";

function truncateText(text: string, maxLength: number) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import Link from "next/link";
import {
getDidFromHandleOrDid,
getVerifiedHandle,
} from "@/lib/data/atproto/did";
} from "@/lib/data/atproto/identity";
import { UserHoverCard } from "@/lib/components/user-hover-card";
import { VariantProps, cva } from "class-variance-authority";
import { cn } from "@/lib/utils";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import "server-only";
import { getDidFromHandleOrDid } from "@/lib/data/atproto/did";
import { getDidFromHandleOrDid } from "@/lib/data/atproto/identity";
import { getPost } from "@/lib/data/db/post";
import { notFound } from "next/navigation";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { notFound } from "next/navigation";
import { PostCard } from "../../../_components/post-card";
import { DeletePostButton } from "./_lib/delete-post-button";
import { getPost } from "@/lib/data/db/post";
import { getDidFromHandleOrDid } from "@/lib/data/atproto/did";
import { getDidFromHandleOrDid } from "@/lib/data/atproto/identity";

type Params = {
postRkey: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { NewComment } from "./_lib/comment-client";
import { Comment } from "./_lib/comment";
import { getCommentsForPost } from "@/lib/data/db/comment";
import { Metadata } from "next";
import { getVerifiedHandle } from "@/lib/data/atproto/did";
import { getVerifiedHandle } from "@/lib/data/atproto/identity";
import { PostPageParams, getPostPageData } from "./_lib/page-data";

export async function generateMetadata({
Expand Down
3 changes: 2 additions & 1 deletion packages/frontpage/app/(app)/post/new/_action.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"use server";

import { DID, getVerifiedHandle } from "@/lib/data/atproto/did";
import { DID } from "@/lib/data/atproto/did";
import { getVerifiedHandle } from "@/lib/data/atproto/identity";
import { createPost } from "@/lib/data/atproto/post";
import { uncached_doesPostExist } from "@/lib/data/db/post";
import { DataLayerError } from "@/lib/data/error";
Expand Down
3 changes: 2 additions & 1 deletion packages/frontpage/app/(app)/profile/[user]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { DID, getDidFromHandleOrDid } from "@/lib/data/atproto/did";
import type { DID } from "@/lib/data/atproto/did";
import { getUserPosts } from "@/lib/data/db/post";
import { unstable_noStore } from "next/cache";
import { notFound } from "next/navigation";
Expand All @@ -14,6 +14,7 @@ import { getBlueskyProfile } from "@/lib/data/user";
import { getUserComments } from "@/lib/data/db/comment";
import { Comment } from "../../post/[postAuthor]/[postRkey]/_lib/comment";
import { Suspense } from "react";
import { getDidFromHandleOrDid } from "@/lib/data/atproto/identity";

type Params = {
user: string;
Expand Down
2 changes: 1 addition & 1 deletion packages/frontpage/app/(auth)/login/_lib/action.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"use server";
import { signIn } from "@/lib/auth";
import { signIn } from "@/lib/auth-sign-in";

export async function loginAction(_prevStart: unknown, formData: FormData) {
const handle = formData.get("handle") as string;
Expand Down
3 changes: 2 additions & 1 deletion packages/frontpage/app/api/hover-card-content/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { badRequest, createApiRoute } from "@/lib/api-route";
import { getVerifiedHandle, parseDid } from "@/lib/data/atproto/did";
import { parseDid } from "@/lib/data/atproto/did";
import { getVerifiedHandle } from "@/lib/data/atproto/identity";
import { getTotalSubmissions } from "@/lib/data/user";

export const GET = createApiRoute(async (request) => {
Expand Down
164 changes: 164 additions & 0 deletions packages/frontpage/lib/auth-sign-in.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import {
calculatePKCECodeChallenge,
discoveryRequest,
generateKeyPair,
generateRandomCodeVerifier,
generateRandomState,
processDiscoveryResponse,
pushedAuthorizationRequest,
} from "oauth4webapi";
import {
getClientMetadata,
getClientPrivateKey,
getOauthClientOptions,
oauthProtectedMetadataRequest,
} from "./auth";
import { getDidFromHandleOrDid } from "./data/atproto/identity";
import { oauthParResponseSchema } from "@atproto/oauth-types";
import { db } from "./db";
import * as schema from "./schema";
import { redirect } from "next/navigation";

export async function signIn(handle: string) {
const did = await getDidFromHandleOrDid(handle);
if (!did) {
return {
error: "DID_NOT_FOUND",
};
}

const meta = await oauthProtectedMetadataRequest(did);
if ("error" in meta) {
return meta;
}

const authServerUrl = meta.data.authorization_servers?.[0];
if (!authServerUrl) {
return {
error: "NO_AUTH_SERVER",
};
}

// TODO: Cache this
const authServer = await processDiscoveryResponse(
new URL(authServerUrl),
await discoveryRequest(new URL(authServerUrl), {
algorithm: "oauth2",
}),
);

// Check this early, we'll need it later
const authorizationEndpiont = authServer.authorization_endpoint;
if (!authorizationEndpiont) {
return {
error: "NO_AUTHORIZATION_ENDPOINT",
};
}

const client = getClientMetadata();

const state = generateRandomState();
const pkceVerifier = generateRandomCodeVerifier();

const dpopKeyPair = await generateKeyPair("RS256", {
extractable: true,
});

const makeParRequest = async (dpopNonce?: string) => {
return pushedAuthorizationRequest(
authServer,
getOauthClientOptions(),
{
response_type: "code",
code_challenge: await calculatePKCECodeChallenge(pkceVerifier),
code_challenge_method: "S256",
// TODO: Do we need this? It's included in the oauth client options
client_id: client.client_id,
state,
redirect_uri: client.redirect_uris[0],
scope: client.scope,
login_hint: handle,
},
{
DPoP: {
privateKey: dpopKeyPair.privateKey,
publicKey: dpopKeyPair.publicKey,
nonce: dpopNonce,
},
clientPrivateKey: await getClientPrivateKey(),
},
);
};

// Try PAR request without DPoP nonce first
// oauth4webapi has an in-memory cache that may be used here
let parResponse = await makeParRequest();

if (!parResponse.ok) {
// TODO: Check for this error when the header is deployed by bsky team
// if (
// // Expect a use_dpop_nonce error
// !parseWwwAuthenticateChallenges(parResponse)?.some(
// (challenge) => challenge.parameters.error === "use_dpop_nonce",
// )
// ) {
// return {
// error: "FAILED_TO_PUSH_AUTHORIZATION_REQUEST",
// };
// }

const dpopNonce = parResponse.headers.get("DPoP-Nonce");
if (!dpopNonce) {
return {
error: "MISSING_PAR_DPOP_NONCE",
};
}
// Try again with new nonce
parResponse = await makeParRequest(dpopNonce);
}

if (!parResponse.ok) {
console.error("PAR error: ", await parResponse.text());
return {
error: "FAILED_TO_PUSH_AUTHORIZATION_REQUEST",
};
}

const dpopNonce = parResponse.headers.get("DPoP-Nonce");

if (!dpopNonce) {
return {
error: "MISSING_PAR_DPOP_NONCE",
};
}

const parResult = oauthParResponseSchema.safeParse(await parResponse.json());
if (!parResult.success) {
return {
error: "INVALID_PAR_RESPONSE",
cause: parResult.error,
};
}

await db.insert(schema.OauthAuthRequest).values({
did: did,
iss: authServer.issuer,
username: handle,
nonce: dpopNonce,
state,
pkceVerifier,
dpopPrivateJwk: JSON.stringify(
await crypto.subtle.exportKey("jwk", dpopKeyPair.privateKey),
),
expiresAt: new Date(Date.now() + 1000 * 60),
createdAt: new Date(),
dpopPublicJwk: JSON.stringify(
await crypto.subtle.exportKey("jwk", dpopKeyPair.publicKey),
),
});

const redirectUrl = new URL(authServer.authorization_endpoint);
redirectUrl.searchParams.set("request_uri", parResult.data.request_uri);
redirectUrl.searchParams.set("client_id", client.client_id);
redirect(redirectUrl.toString());
}
Loading

0 comments on commit 14b31d7

Please sign in to comment.