-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support pubkey for auth #9
Merged
Merged
Changes from 4 commits
Commits
Show all changes
24 commits
Select commit
Hold shift + click to select a range
b0767c5
feat: add NIP-07 extension login
bob2402 44420c8
verify Event
bob2402 c70db5a
feat: support password and pubkey for auth
bob2402 a0c907a
update
bob2402 e71e242
add more verify logic
bob2402 e3c4fd1
support multiple pubkey
bob2402 80e9407
update
bob2402 686e92c
update
bob2402 7207b72
chore: remove password
bob2402 e2fe1b6
test: update
bob2402 25e9e3b
relayed_pubkey
bob2402 635cae1
logined
bob2402 2194864
update
bob2402 15dfdd3
fix relay information pubkey type
bob2402 22aefb6
add error handling
bob2402 e2d2215
test type
bob2402 9ae1685
update
bob2402 45bfe4d
graphql test
bob2402 fb8bc01
update
bob2402 f7ff121
+
bob2402 b3178c0
fix token decode error
bob2402 85ed4dc
refactor
BlowaterNostr 8791ccb
+
BlowaterNostr 6453e77
fix test
bob2402 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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 |
---|---|---|
|
@@ -5,7 +5,7 @@ import { RootResolver } from "./resolvers/root.ts"; | |
import * as gql from "https://esm.sh/graphql@16.8.1"; | ||
import { Policy } from "./resolvers/policy.ts"; | ||
import { func_ResolvePolicyByKind } from "./resolvers/policy.ts"; | ||
import { NostrKind, PublicKey } from "./_libs.ts"; | ||
import { NostrKind, PublicKey, verifyEvent } from "./_libs.ts"; | ||
import { PolicyStore } from "./resolvers/policy.ts"; | ||
import { Policies } from "./resolvers/policy.ts"; | ||
import { | ||
|
@@ -34,7 +34,7 @@ export type DefaultPolicy = { | |
export type Relay = { | ||
server: Deno.HttpServer; | ||
url: string; | ||
password: string; | ||
password?: string; | ||
shutdown: () => Promise<void>; | ||
set_policy: (args: { | ||
kind: NostrKind; | ||
|
@@ -57,13 +57,6 @@ export async function run(args: { | |
kv?: Deno.Kv; | ||
}): Promise<Error | Relay> { | ||
const connections = new Map<WebSocket, SubscriptionMap>(); | ||
let { password } = args; | ||
if (password == undefined) { | ||
password = Deno.env.get("relayed_pw"); | ||
if (!password) { | ||
return new Error("password is not set, please set env var $relayed_pw"); | ||
} | ||
} | ||
if (args.kv == undefined) { | ||
args.kv = await Deno.openKv(); | ||
} | ||
|
@@ -82,6 +75,22 @@ export async function run(args: { | |
default_information, | ||
); | ||
|
||
let { password } = args; | ||
if (!password) { | ||
const { pubkey } = await relayInformationStore.resolveRelayInformation(); | ||
if (!pubkey) { | ||
const env_pubkey = Deno.env.get("relayed_pubkey"); | ||
if (!env_pubkey) { | ||
password = Deno.env.get("relayed_pw"); | ||
if (!password) { | ||
return new Error( | ||
"password or pubkey is not set, please set env var $relayed_pw or $relayed_pubkey", | ||
); | ||
} | ||
} | ||
} | ||
} | ||
|
||
const eventStore = await EventStore.New(args.kv); | ||
|
||
const server = Deno.serve( | ||
|
@@ -140,7 +149,7 @@ export type EventReadWriter = { | |
|
||
const root_handler = ( | ||
args: { | ||
password: string; | ||
password?: string; | ||
information?: RelayInformation; | ||
connections: Map<WebSocket, SubscriptionMap>; | ||
default_policy: DefaultPolicy; | ||
|
@@ -154,6 +163,12 @@ async (req: Request, info: Deno.ServeHandlerInfo) => { | |
console.log(info.remoteAddr); | ||
|
||
const { pathname, protocol } = new URL(req.url); | ||
if (pathname === "/api/auth/login") { | ||
const auth = req.headers.get("authorization"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. need to validate it |
||
const resp = new Response("ok"); | ||
resp.headers.set("set-cookie", `token=${auth}; Path=/; Secure; HttpOnly; SameSite=Strict;`); | ||
return resp; | ||
} | ||
if (pathname == "/api") { | ||
return graphql_handler(args)(req); | ||
} | ||
|
@@ -175,27 +190,62 @@ async (req: Request, info: Deno.ServeHandlerInfo) => { | |
|
||
const graphql_handler = ( | ||
args: { | ||
password: string; | ||
password?: string; | ||
kv: Deno.Kv; | ||
policyStore: PolicyStore; | ||
relayInformationStore: RelayInformationStore; | ||
}, | ||
) => | ||
async (req: Request) => { | ||
const { password, policyStore } = args; | ||
if (req.method == "POST") { | ||
const query = await req.json(); | ||
const pw = req.headers.get("password"); | ||
if (pw != password) { | ||
return new Response(`{"errors":"incorrect password"}`); | ||
try { | ||
const query = await req.json(); | ||
if (!args.password) { | ||
const cookie = req.headers.get("cookie"); | ||
const token = cookie?.split(";").find((c) => c.includes("token"))?.split("=")[1].split( | ||
" ", | ||
)[1]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This line is not readable at all. |
||
const event = token ? JSON.parse(atob(token)) : undefined; | ||
if (event) { | ||
if (!await verifyEvent(event)) { | ||
return new Response(`{"errors":"token not verified"}`); | ||
} | ||
const { pubkey } = await args.relayInformationStore.resolveRelayInformation(); | ||
if (!pubkey) { | ||
return new Response(`{"errors":"relay pubkey not set"}`); | ||
} | ||
const relayPubkey = PublicKey.FromString(pubkey); | ||
if (relayPubkey instanceof Error) { | ||
return new Response(`{"errors":"relay pubkey not valid"}`); | ||
} | ||
if (event.pubkey != relayPubkey.hex) { | ||
return new Response(`{"errors":"you are not admin"}`); | ||
} | ||
const result = await gql.graphql({ | ||
schema: schema, | ||
source: query.query, | ||
variableValues: query.variables, | ||
rootValue: RootResolver(args), | ||
}); | ||
return new Response(JSON.stringify(result)); | ||
} | ||
return new Response(`{"errors":"please login first"}`); | ||
} else { | ||
const password = req.headers.get("password"); | ||
if (password != args.password) { | ||
return new Response(`{"errors":"password not correct"}`); | ||
} | ||
const result = await gql.graphql({ | ||
schema: schema, | ||
source: query.query, | ||
variableValues: query.variables, | ||
rootValue: RootResolver(args), | ||
}); | ||
return new Response(JSON.stringify(result)); | ||
} | ||
} catch (error) { | ||
return new Response(`{"errors":"${error}"}`); | ||
} | ||
const result = await gql.graphql({ | ||
schema: schema, | ||
source: query.query, | ||
variableValues: query.variables, | ||
rootValue: RootResolver(args), | ||
}); | ||
return new Response(JSON.stringify(result)); | ||
} else if (req.method == "GET") { | ||
const res = new Response(graphiql); | ||
res.headers.set("content-type", "html"); | ||
|
@@ -293,6 +343,7 @@ const graphiql = ` | |
</head> | ||
|
||
<body> | ||
<button id="nip7">Login with NIP-07 extensions</button> | ||
<div id="graphiql">Loading...</div> | ||
<script> | ||
const root = ReactDOM.createRoot(document.getElementById('graphiql')); | ||
|
@@ -307,6 +358,33 @@ const graphiql = ` | |
plugins: [explorerPlugin], | ||
}), | ||
); | ||
const nip7 = document.getElementById('nip7'); | ||
nip7.onclick = async () => { | ||
if ("nostr" in window) { | ||
try { | ||
const ext = window.nostr; | ||
const pubkey = await ext.getPublicKey(); | ||
const unsigned_event = { | ||
pubkey, | ||
content: "", | ||
created_at: Math.floor(Date.now() / 1000), | ||
kind: 27235, | ||
tags: [], | ||
} | ||
const event = await ext.signEvent(unsigned_event); | ||
fetch('/api/auth/login', { | ||
headers: { | ||
authorization: "Nostr " + btoa(JSON.stringify(event)), | ||
}, | ||
credentials: 'include' | ||
}) | ||
} catch (e) { | ||
console.error(e); | ||
} | ||
} else { | ||
alert("Nostr extension not found"); | ||
} | ||
}; | ||
</script> | ||
</body> | ||
</html>`; |
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
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
need to check the validity of the public key