an attempt to make a lightweight, easily self-hostable, scoped bluesky appview
1// kindly borrowed from https://github.com/usounds/Skyblur/blob/303793148a6373b313d3354a453876cad864eae2/backend/src/logic/JWTTokenHandler.ts#L2
2// MIT License
3import * as didJWT from "npm:did-jwt"
4import { DIDDocument, DIDResolutionOptions, ParsedDID, Resolver, } from 'npm:did-resolver';
5import { DIDResolver, ResolverRegistry } from 'npm:did-resolver';
6import { getResolver as getWebResolver } from 'npm:web-did-resolver';
7
8export function getResolver() {
9 async function resolve(
10 did: string,
11 parsed: ParsedDID,
12 didResolver: Resolver,
13 options: DIDResolutionOptions
14 ): Promise<DIDDocument> {
15 const encodedDid = encodeURIComponent(did);
16 const didUrl = `https://plc.directory/${encodedDid}`;
17 const response = await fetch(didUrl);
18 const didDoc = await response.json() as DIDDocument
19 return didDoc
20 }
21
22 return { DidPlcResolver: resolve }
23}
24const myResolver = getResolver()
25const webResolver = getWebResolver()
26const resolver: ResolverRegistry = {
27 'plc': myResolver.DidPlcResolver as unknown as DIDResolver,
28 ...webResolver
29}
30export const resolverInstance = new Resolver(resolver)
31export type Service = {
32 id: string;
33 type: string;
34 serviceEndpoint: string | Record<string, any> | Array<Record<string, any>>;
35}
36
37export const verifyJWT = async (auth: string, audience:string) => {
38 const authorization = auth.replace('Bearer ', '').trim()
39 const decodedJWT = authorization.replace('Bearer ', '').trim()
40
41 console.log("audience is: ", audience)
42
43 const result = await didJWT.verifyJWT(decodedJWT, {
44 resolver: resolverInstance,
45 audience: audience
46 })
47
48 return result
49
50}
51export const fetchDiDDocument = async (did: string) => {
52 try {
53 const didDocument = await resolverInstance.resolve(did)
54 return didDocument
55 } catch (error) {
56 console.error('Error fetching service endpoint:', error);
57 }
58
59
60};
61
62export const fetchServiceEndpoint = async (did: string) => {
63 try {
64 const response = await fetchDiDDocument(did);
65 if (!response) {
66 throw new Error('Invalid DID document response');
67 }
68
69 const didDocument = response as unknown as DIDDocument;
70
71 // didDocument.serviceが存在するかチェック
72 const service = didDocument.service?.find((s: Service) => s.id === '#atproto_pds');
73
74 if (service && service.serviceEndpoint) {
75 return service.serviceEndpoint;
76 } else {
77 throw new Error('Service with id #atproto_pds not found or no service endpoint available');
78 }
79 } catch (error) {
80 console.error('Error fetching service endpoint:', error);
81 }
82};