Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions ui/.env
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
KEYCLOAK_CLIENT_ID="dreams"
KEYCLOAK_REALM=plato
DATABASE_URL='postgres://postgres:postgres@localhost:35432/cobudget'

# DATABASE_URL='postgres://postgres:postgres@localhost:35432/cobudget'
SINGLE_GROUP_MODE=false
BUCKET_NAME_SINGULAR="bucket"
BUCKET_NAME_PLURAL="buckets"
PLATFORM_NAME="Cobudget"
PLATFORM_LOGO="https://cobudget.com/logo_400.jpg"

COOKIE_SECRET="A_LONG_RANDOM_VALUE_THAT_IS_NOT_THIS_STRING"
POSTMARK_API_TOKEN="fake"
MAGIC_LINK_SECRET="fake"
FROM_EMAIL="fake@example.com"
2 changes: 1 addition & 1 deletion ui/.env.development
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ REALITIES_DEPLOY_URL="localhost:2550"
REACT_APP_GRAPHQL_ENDPOINT=http://localhost:3100/graphql
REACT_APP_GRAPHQL_SUBSCRIPTION=ws://localhost:3100/graphql

DATABASE_URL='postgres://postgres:postgres@localhost:35432/cobudget'
DATABASE_URL='postgresql://postgres:vWDXHXpIZytwF3lF@db.zdderyqdnmrgwrtaefxa.supabase.co:5432/postgres'
NEXTAUTH_URL=http://localhost:3000

# For the Discourse integration, don't use these in production
Expand Down
31 changes: 23 additions & 8 deletions ui/components/AuthenticationForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import TextField from "./TextField";
import Button from "./Button";
import Banner from "components/Banner";
import { FormattedMessage, useIntl } from "react-intl";
import Web3 from "web3";

export default function AuthenticationForm({
fbEmailError = false,
Expand All @@ -23,9 +24,11 @@ export default function AuthenticationForm({
const redirect = r?.toString();
const intl = useIntl();



return (
<div>
<form
<form
onSubmit={(evt) => {
evt.preventDefault();
setLoading(true);
Expand Down Expand Up @@ -77,14 +80,26 @@ export default function AuthenticationForm({
>
<FormattedMessage defaultMessage="Send magic link" />
</Button>
</form>
</form>

{(fbLoginEnabled || googleLoginEnabled) && (
{(!fbLoginEnabled || googleLoginEnabled) && (
<div className="w-full h-px bg-gray-300 my-5"></div>
)}

{fbLoginEnabled && (
<div>
{
<div className="mt-4">
<Button
fullWidth
href={"/api/auth/web3/"}
className="text-center"
style={{ backgroundColor: "#040404" }}
>
<FormattedMessage defaultMessage="Log in with Wallet" />
</Button>
</div>
}
{!fbLoginEnabled && (
<div className="mt-4">
{fbEmailError && (
<Banner
className={"mb-4"}
Expand All @@ -102,7 +117,7 @@ export default function AuthenticationForm({
)}
<Button
fullWidth
href={`/api/auth/facebook/?${
href={`/api/auth/facebooks/?${
fbEmailError ? "fb_no_email_scope=true&" : ""
}remember_me=true&${redirect ? `r=${redirect}` : ""}`}
className="text-center"
Expand All @@ -112,7 +127,7 @@ export default function AuthenticationForm({
</Button>
</div>
)}
{googleLoginEnabled && (
{!googleLoginEnabled && (
<div>
<Button
fullWidth
Expand All @@ -128,4 +143,4 @@ export default function AuthenticationForm({
)}
</div>
);
}
};
3 changes: 3 additions & 0 deletions ui/components/Header/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,9 @@ const Header = ({ currentUser, fetchingUser, group, round, bucket, ss }) => {
<NavItem href={`/signup`} roundColor={color} primary>
<FormattedMessage defaultMessage="Sign up" />
</NavItem>
{/* <NavItem href={`/web3-login`} roundColor={color} primary>
<FormattedMessage defaultMessage="Login with wallet" />
</NavItem> */}
<div className="sm:hidden">
<hr className="mt-4 mb-2 mx-4 opacity-25" />
<LandingPageLinks desktop={false} />
Expand Down
4 changes: 3 additions & 1 deletion ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"crowdin:upload": "../scripts/crowdin.sh upload"
},
"engines": {
"node": "14.x"
"node": "18.x"
},
"dependencies": {
"@apollo/client": "^3.4.11",
Expand Down Expand Up @@ -81,6 +81,7 @@
"passport-facebook": "3.0.0",
"passport-google-oauth20": "2.0.0",
"passport-magic-login": "^1.0.8",
"passport-web3": "^0.1.7",
"postcss": "^8.2.10",
"postmark": "2.9.0",
"prop-types": "^15.7.2",
Expand Down Expand Up @@ -116,6 +117,7 @@
"unist-util-visit": "4.1.0",
"urql": "2.2.0",
"validator": "13.7.0",
"web3": "^1.9.0",
"yup": "^0.29.3"
},
"devDependencies": {
Expand Down
1 change: 1 addition & 0 deletions ui/pages/api/auth/magiclink/callback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ import handler from "../../../../server/api-handler";
export default handler()
.use(passport.authenticate("magiclogin"))
.use((req, res) => {
console.log(req.user, req.user?.redirect || "/")
res.redirect(req.user?.redirect || "/");
});
33 changes: 33 additions & 0 deletions ui/pages/api/auth/web3/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import nextConnect from 'next-connect';
import passport from 'passport';
import { setLoginSession, getLoginSession } from './lib/auth';

const handler = nextConnect();

handler.use(passport.initialize());

handler.post(async (req, res) => {
try {
const user = await authenticateWeb3User(req, res);
const session = { ...user };
await setLoginSession(res, session);
res.status(200).json({ user });
} catch (error) {
res.status(401).json({ error: 'Unauthorized' });
}
});

const authenticateWeb3User = (req, res) =>
new Promise((resolve, reject) => {
passport.authenticate('web3', (error, user) => {
if (error) {
return reject(error);
}
if (!user) {
return reject(new Error('Unauthorized'));
}
return resolve(user);
})(req, res);
});

export default handler;
1 change: 1 addition & 0 deletions ui/pages/api/auth/web3/callback.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// redirect to users dashboard
35 changes: 35 additions & 0 deletions ui/pages/api/auth/web3/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@

import nextConnect from 'next-connect';
import passport from 'passport';
import { setLoginSession, getLoginSession } from './lib/auth';
import callback from "./callback"

const handler = nextConnect();

handler.use(passport.initialize());

handler.post(async (req, res) => {
try {
const user = await authenticateWeb3User(req, res);
const session = { ...user };
await setLoginSession(res, session);
res.status(200).json({ user });
} catch (error) {
res.status(401).json({ error: 'Unauthorized' });
}
});

const authenticateWeb3User = (req, res) =>
new Promise((resolve, reject) => {
passport.authenticate('web3', (error, user) => {
if (error) {
return reject(error);
}
if (!user) {
return reject(new Error('Unauthorized'));
}
return resolve(user);
})(req, res);
});

export default handler;
25 changes: 25 additions & 0 deletions ui/pages/api/auth/web3/lib/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import cookie from 'cookie';
import jwt from 'jsonwebtoken';

export const setLoginSession = (res, session) => {
const sessionData = JSON.stringify(session);
const sessionToken = jwt.sign(sessionData, process.env.JWT_SECRET);

res.setHeader('Set-Cookie', cookie.serialize('session', sessionToken, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
maxAge: 60 * 60 * 24 * 7 // 1 week
}));
};

export const getLoginSession = (req) => {
const sessionToken = req.cookies.session;
if (!sessionToken) return null;

try {
const sessionData = jwt.verify(sessionToken, process.env.JWT_SECRET);
return JSON.parse(sessionData);
} catch (error) {
return null;
}
};
30 changes: 30 additions & 0 deletions ui/pages/api/auth/web3/lib/passport.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import passport from 'passport';
import { Strategy as LocalStrategy } from 'passport-local';
import { getWeb3 } from './web3';
import prisma from '../prisma/client';

passport.use(
'web3',
new LocalStrategy(async (username, signature, done) => {
try {
const web3 = await getWeb3();
const recoveredAddress = web3.eth.accounts.recover(username, signature);

// Check if the user exists in the database
let user = await prisma.user.findUnique({
where: { ethereumAddress: recoveredAddress },
});

// If the user does not exist, create a new user
if (!user) {
user = await prisma.user.create({
data: { ethereumAddress: recoveredAddress },
});
}

return done(null, user);
} catch (error) {
return done(error);
}
})
);
24 changes: 24 additions & 0 deletions ui/pages/api/auth/web3/lib/web3.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import Web3 from 'web3';
import HDWalletProvider from '@truffle/hdwallet-provider';

const getProvider = async () => {
if (typeof window !== 'undefined') {
if (window.ethereum) {
await window.ethereum.enable();
return window.ethereum;
} else if (window.web3) {
return window.web3.currentProvider;
} else {
window.alert('Please install MetaMask!');
}
} else {
const mnemonic = process.env.MNEMONIC;
const rpcUrl = process.env.RPC_URL;
return new HDWalletProvider(mnemonic, rpcUrl);
}
};

export const getWeb3 = async () => {
const provider = await getProvider();
return new Web3(provider);
};
2 changes: 2 additions & 0 deletions ui/server/passport/magicLink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { createOrGetUser } from "./helpers";
if (!process.env.MAGIC_LINK_SECRET)
throw new Error(`Add MAGIC_LINK_SECRET environment variable`);

console.log(process.env.MAGIC_LINK_SECRET, process.env.DATABASE_URL);

const magicLink = new MagicLoginStrategy({
secret: process.env.MAGIC_LINK_SECRET,
callbackUrl: "/api/auth/magiclink/callback",
Expand Down
5 changes: 5 additions & 0 deletions ui/server/prisma/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@ if (process.env.NODE_ENV === "production") {
if (!global.cachedPrisma) {
global.cachedPrisma = new PrismaClient();
}

prisma = global.cachedPrisma;

}
// console.log({prisma});
// prisma = new PrismaClient();
// console.log({prisma},2);

export default prisma;

This file was deleted.

This file was deleted.

This file was deleted.

Loading