-
Notifications
You must be signed in to change notification settings - Fork 44
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
MagicV2 + Farcaster login + SMS login #9913
base: master
Are you sure you want to change the base?
Changes from 3 commits
e95ae60
ff072aa
9bbd868
96e654b
3709179
400ab68
afe7e98
808a75b
84ce500
2071f2c
bceca47
2990d53
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,29 +1,27 @@ | ||
/** | ||
* @file Manages logged-in user accounts and local storage. | ||
*/ | ||
import { SIWESigner } from '@canvas-js/chain-ethereum'; | ||
import { Session } from '@canvas-js/interfaces'; | ||
|
||
import { ExtendedCommunity } from '@hicommonwealth/schemas'; | ||
import { | ||
CANVAS_TOPIC, | ||
ChainBase, | ||
chainBaseToCanvasChainId, | ||
getSessionSigners, | ||
serializeCanvas, | ||
WalletId, | ||
WalletSsoSource, | ||
chainBaseToCanvasChainId, | ||
} from '@hicommonwealth/shared'; | ||
import { CosmosExtension } from '@magic-ext/cosmos'; | ||
import { OAuthExtension } from '@magic-ext/oauth2'; | ||
import axios from 'axios'; | ||
import { notifyError } from 'controllers/app/notifications'; | ||
import { getMagicCosmosSessionSigner } from 'controllers/server/sessions'; | ||
import { isSameAccount } from 'helpers'; | ||
|
||
import { getSessionSigners } from '@hicommonwealth/shared'; | ||
import { initAppState } from 'state'; | ||
|
||
import { SIWESigner } from '@canvas-js/chain-ethereum'; | ||
import { Session } from '@canvas-js/interfaces'; | ||
import { CANVAS_TOPIC, serializeCanvas } from '@hicommonwealth/shared'; | ||
import { CosmosExtension } from '@magic-ext/cosmos'; | ||
import { OAuthExtension } from '@magic-ext/oauth'; | ||
import { Magic } from 'magic-sdk'; | ||
|
||
import { ExtendedCommunity } from '@hicommonwealth/schemas'; | ||
import axios from 'axios'; | ||
import app from 'state'; | ||
import app, { initAppState } from 'state'; | ||
import { EXCEPTION_CASE_VANILLA_getCommunityById } from 'state/api/communities/getCommuityById'; | ||
import { SERVER_URL } from 'state/api/config'; | ||
import { | ||
|
@@ -34,6 +32,7 @@ import { | |
import { welcomeOnboardModal } from 'state/ui/modals/welcomeOnboardModal'; | ||
import { userStore } from 'state/ui/user'; | ||
import { z } from 'zod'; | ||
import { defaultMagic } from '../../App'; | ||
import Account from '../../models/Account'; | ||
import AddressInfo from '../../models/AddressInfo'; | ||
import type BlockInfo from '../../models/BlockInfo'; | ||
|
@@ -287,24 +286,27 @@ export async function createUserWithAddress( | |
} | ||
|
||
async function constructMagic(isCosmos: boolean, chain?: string) { | ||
if (!isCosmos) { | ||
return defaultMagic; | ||
} | ||
|
||
if (isCosmos && !chain) { | ||
throw new Error('Must be in a community to sign in with Cosmos magic link'); | ||
} | ||
|
||
if (process.env.MAGIC_PUBLISHABLE_KEY === undefined) { | ||
throw new Error('Missing magic key'); | ||
} | ||
|
||
return new Magic(process.env.MAGIC_PUBLISHABLE_KEY, { | ||
extensions: !isCosmos | ||
? [new OAuthExtension()] | ||
: [ | ||
new OAuthExtension(), | ||
new CosmosExtension({ | ||
// Magic has a strict cross-origin policy that restricts rpcs to whitelisted URLs, | ||
// so we can't use app.chain.meta?.node?.url | ||
rpcUrl: `${document.location.origin}${SERVER_URL}/magicCosmosProxy/${chain}`, | ||
}), | ||
], | ||
extensions: [ | ||
new OAuthExtension(), | ||
new CosmosExtension({ | ||
// Magic has a strict cross-origin policy that restricts rpcs to whitelisted URLs, | ||
// so we can't use app.chain.meta?.node?.url | ||
rpcUrl: `${document.location.origin}${SERVER_URL}/magicCosmosProxy/${chain}`, | ||
}), | ||
], | ||
}); | ||
} | ||
|
||
|
@@ -333,12 +335,21 @@ export async function startLoginWithMagicLink({ | |
walletSsoSource: WalletSsoSource.Email, | ||
}); | ||
|
||
return { bearer, address }; | ||
} else if (provider === WalletSsoSource.Farcaster) { | ||
const bearer = await magic.farcaster.login(); | ||
|
||
const { address } = await handleSocialLoginCallback({ | ||
bearer, | ||
walletSsoSource: WalletSsoSource.Farcaster, | ||
}); | ||
|
||
Comment on lines
+354
to
+361
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. do we need to strictly follow this? can we reuse the 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. It's a little bit confusing but farcaster is not technically a social login. This is social logins require to use the webhook callback which farcaster does not use since it is decentralized. There is no entity (Like google or whatever) to submit the callback webhook for us. This basically takes the same path as email login, the only difference is that it needs to call the magic farcaster login method |
||
return { bearer, address }; | ||
} else { | ||
const params = `?redirectTo=${ | ||
redirectTo ? encodeURIComponent(redirectTo) : '' | ||
}&chain=${chain || ''}&sso=${provider}`; | ||
await magic.oauth.loginWithRedirect({ | ||
await magic.oauth2.loginWithRedirect({ | ||
provider, | ||
redirectURI: new URL( | ||
'/finishsociallogin' + params, | ||
|
@@ -409,7 +420,7 @@ export async function handleSocialLoginCallback({ | |
// Code up to this line might run multiple times because of extra calls to useEffect(). | ||
// Those runs will be rejected because getRedirectResult purges the browser search param. | ||
let profileMetadata, magicAddress; | ||
if (isEmail) { | ||
if (isEmail || walletSsoSource === WalletSsoSource.Farcaster) { | ||
const metadata = await magic.user.getMetadata(); | ||
profileMetadata = { username: null }; | ||
|
||
|
@@ -423,7 +434,7 @@ export async function handleSocialLoginCallback({ | |
magicAddress = utils.getAddress(metadata.publicAddress); | ||
} | ||
} else { | ||
const result = await magic.oauth.getRedirectResult(); | ||
const result = await magic.oauth2.getRedirectResult(); | ||
|
||
if (!bearer) { | ||
console.log('No bearer token found in magic redirect result'); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -57,6 +57,7 @@ const SSO_OPTIONS: AuthSSOs[] = [ | |
'apple', | ||
'github', | ||
'email', | ||
'farcaster', | ||
] as const; | ||
|
||
/** | ||
|
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.
This might not be the best place to put it, but it needs to be instantiated when the app starts. Any better suggestions?
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.
not sure but does this cause circular dependency issues? or maybe we could add to
packages/commonwealth/client/scripts/controllers/app/login.ts
(that needs it - also login.ts is imported in many init flow conditions)?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.
Yeah I moved it to login.ts. And yes this is the cause of the test failure issue. Not exactly a circular dependency but more of this Magic constructor calls the DOM, but our tests don't have the DOM so it throws an error.
But basically this is needed because their constructor needs to complete an async request before we can call login. So basically the earlier it is instantiated the better, but still no guarantee that login won't fail when it is called.