mirror of
https://github.com/aljazceru/landscape-template.git
synced 2025-12-30 12:34:19 +01:00
refactor: auth funcs
This commit is contained in:
@@ -20,52 +20,61 @@ async function login(tag, k1, sig, key) {
|
||||
return { status: 'ERROR', reason: 'Not a login request' }
|
||||
}
|
||||
|
||||
const result = LnurlService.verifySig(sig, k1, key)
|
||||
if (!result) {
|
||||
try {
|
||||
await LnurlService.verifySig(sig, k1, key)
|
||||
} catch (error) {
|
||||
return { status: 'ERROR', reason: 'Invalid Signature' }
|
||||
}
|
||||
|
||||
const user = await prisma.user.findFirst({ where: { pubKey: key } })
|
||||
if (user === null) {
|
||||
await prisma.user.create({
|
||||
data: {
|
||||
pubKey: key,
|
||||
name: key,
|
||||
avatar: `https://avatars.dicebear.com/api/bottts/${key}.svg`
|
||||
}
|
||||
|
||||
try {
|
||||
const user = await prisma.user.findFirst({ where: { pubKey: key } })
|
||||
if (user === null) {
|
||||
await prisma.user.create({
|
||||
data: {
|
||||
pubKey: key,
|
||||
name: key,
|
||||
avatar: `https://avatars.dicebear.com/api/bottts/${key}.svg`
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Set cookies on the user's headers
|
||||
const hour = 3600000
|
||||
const maxAge = 30 * 24 * hour
|
||||
const jwtSecret = CONSTS.JWT_SECRET;
|
||||
|
||||
LnurlService.removeHash(LnurlService.createHash(k1)).catch();
|
||||
LnurlService.removeExpiredHashes().catch();
|
||||
|
||||
const jwt = await new jose.SignJWT({ pubKey: key })
|
||||
.setProtectedHeader({ alg: 'HS256' })
|
||||
.setIssuedAt()
|
||||
.setExpirationTime(maxAge)
|
||||
//TODO: Set audience, issuer
|
||||
.sign(Buffer.from(jwtSecret, 'utf-8'))
|
||||
|
||||
|
||||
|
||||
const authCookie = cookie.serialize('Authorization', `Bearer ${jwt}`, {
|
||||
secure: true,
|
||||
httpOnly: true,
|
||||
path: '/',
|
||||
maxAge: maxAge,
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
status: 'OK',
|
||||
'headers': {
|
||||
'Set-Cookie': authCookie,
|
||||
'Cache-Control': 'no-cache',
|
||||
},
|
||||
}
|
||||
} catch (error) {
|
||||
return { status: 'ERROR', reason: 'Unexpected error happened, please try again' }
|
||||
|
||||
|
||||
// Set cookies on the user's headers
|
||||
const hour = 3600000
|
||||
const maxAge = 30 * 24 * hour
|
||||
const jwtSecret = CONSTS.JWT_SECRET;
|
||||
|
||||
|
||||
const jwt = await new jose.SignJWT({ pubKey: key })
|
||||
.setProtectedHeader({ alg: 'HS256' })
|
||||
.setIssuedAt()
|
||||
.setExpirationTime(maxAge)
|
||||
//TODO: Set audience, issuer
|
||||
.sign(Buffer.from(jwtSecret, 'utf-8'))
|
||||
|
||||
|
||||
|
||||
const authCookie = cookie.serialize('Authorization', `Bearer ${jwt}`, {
|
||||
secure: true,
|
||||
httpOnly: true,
|
||||
path: '/',
|
||||
maxAge: maxAge,
|
||||
})
|
||||
|
||||
return {
|
||||
status: 'OK',
|
||||
'headers': {
|
||||
'Set-Cookie': authCookie,
|
||||
'Cache-Control': 'no-cache',
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ function isHashUsed(hash) {
|
||||
}
|
||||
|
||||
function addHash(hash) {
|
||||
prisma.generatedK1.create({
|
||||
return prisma.generatedK1.create({
|
||||
data: {
|
||||
value: hash,
|
||||
}
|
||||
@@ -37,7 +37,7 @@ function addHash(hash) {
|
||||
}
|
||||
|
||||
function removeHash(hash) {
|
||||
prisma.generatedK1.delete({
|
||||
return prisma.generatedK1.delete({
|
||||
where: {
|
||||
value: hash,
|
||||
}
|
||||
@@ -48,7 +48,7 @@ function removeExpiredHashes() {
|
||||
const now = new Date();
|
||||
const lastHourDate = new Date(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours() - 1, now.getMinutes());
|
||||
|
||||
prisma.generatedK1.deleteMany({
|
||||
return prisma.generatedK1.deleteMany({
|
||||
where: {
|
||||
createdAt: {
|
||||
lt: lastHourDate
|
||||
@@ -60,7 +60,7 @@ function removeExpiredHashes() {
|
||||
async function generateAuthUrl() {
|
||||
const hostname = CONSTS.LNURL_AUTH_HOST;
|
||||
const secret = await generateSecret()
|
||||
addHash(createHash(secret))
|
||||
await addHash(createHash(secret))
|
||||
const url = `${hostname}?tag=login&k1=${secret}`
|
||||
return {
|
||||
url,
|
||||
@@ -69,12 +69,15 @@ async function generateAuthUrl() {
|
||||
}
|
||||
}
|
||||
|
||||
function verifySig(sig, k1, key) {
|
||||
async function verifySig(sig, k1, key) {
|
||||
if (!lnurl.verifyAuthorizationSignature(sig, k1, key)) {
|
||||
const message = 'Signature verification failed'
|
||||
throw new Error(message)
|
||||
}
|
||||
const hash = createHash(k1)
|
||||
const hashExist = await isHashUsed(hash);
|
||||
if (!hashExist)
|
||||
throw new Error('Provided k1 is not issued by server')
|
||||
return { key, hash }
|
||||
}
|
||||
|
||||
@@ -94,5 +97,6 @@ module.exports = {
|
||||
generateAuthUrl: generateAuthUrl,
|
||||
verifySig: verifySig,
|
||||
removeHash: removeHash,
|
||||
createHash: createHash,
|
||||
removeExpiredHashes: removeExpiredHashes
|
||||
}
|
||||
@@ -4,25 +4,25 @@ const cookie = require('cookie')
|
||||
const jose = require('jose');
|
||||
const { CONSTS } = require('../utils');
|
||||
|
||||
const extractKey = async (cookieHeader) => {
|
||||
const cookies = cookie.parse(cookieHeader ?? '');
|
||||
const authToken = cookies.Authorization;
|
||||
if (authToken) {
|
||||
const token = authToken.split(' ')[1];
|
||||
const { payload } = await jose.jwtVerify(token, Buffer.from(CONSTS.JWT_SECRET), {
|
||||
algorithms: ['HS256'],
|
||||
})
|
||||
return payload.pubKey
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
const server = new ApolloServer({
|
||||
schema,
|
||||
|
||||
context: async ({ event }) => {
|
||||
const cookies = cookie.parse(event.headers.Cookie ?? '');
|
||||
const authToken = cookies.Authorization;
|
||||
if (authToken) {
|
||||
const token = authToken.split(' ')[1];
|
||||
const { payload } = await jose.jwtVerify(token, Buffer.from(CONSTS.JWT_SECRET), {
|
||||
algorithms: ['HS256'],
|
||||
})
|
||||
return { userPubKey: payload.pubKey }
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
};
|
||||
const userPubKey = await extractKey(event.headers.Cookie)
|
||||
return { userPubKey, }
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useMountEffect } from "@react-hookz/web";
|
||||
import { useEffect, useState } from "react"
|
||||
import { BsFillLightningChargeFill } from "react-icons/bs";
|
||||
import { Grid } from "react-loader-spinner";
|
||||
@@ -5,6 +6,14 @@ import { useNavigate } from "react-router-dom";
|
||||
import { useMeQuery } from "src/graphql"
|
||||
|
||||
|
||||
|
||||
const getLnurlAuth = async () => {
|
||||
const res = await fetch(process.env.REACT_APP_AUTH_END_POINT! + '/login')
|
||||
const data = await res.json()
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
export default function LoginPage() {
|
||||
const [loadingLnurl, setLoadingLnurl] = useState(true)
|
||||
const [lnurlAuth, setLnurlAuth] = useState("");
|
||||
@@ -27,14 +36,12 @@ export default function LoginPage() {
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const res = await fetch(process.env.REACT_APP_AUTH_END_POINT! + '/login')
|
||||
const data = await res.json()
|
||||
useMountEffect(() => {
|
||||
getLnurlAuth().then(data => {
|
||||
setLoadingLnurl(false);
|
||||
setLnurlAuth(data.encoded)
|
||||
})();
|
||||
}, [])
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
|
||||
@@ -63,7 +70,7 @@ export default function LoginPage() {
|
||||
<p className="text-body1 font-bolder text-center">
|
||||
Login
|
||||
</p>
|
||||
<p className="text-gray-600 text-body-4">
|
||||
<p className="text-gray-600 text-body4 text-center">
|
||||
Zero credentials authentication.
|
||||
<br />
|
||||
All you need is a connected <a href='https://getalby.com'
|
||||
|
||||
Reference in New Issue
Block a user