spruceid / ssx Goto Github PK
View Code? Open in Web Editor NEWSelf-Sovereign Anything
Home Page: https://www.sprucekit.dev/ssx/ssx-overview
License: Apache License 2.0
Self-Sovereign Anything
Home Page: https://www.sprucekit.dev/ssx/ssx-overview
License: Apache License 2.0
wagmi v1 was released recently, replacing ethers
with viem
, supporting BigInt
natively, removing Goerli (deprecated testnet) support, and other useful changes.
Migration guide: https://wagmi.sh/core/migration-guide
As a developer, I would like ssx to use wagmi v1 to take advantage of its modern features (and without needing to implement workarounds to juggle multiple versions of wagmi, such as peer deps).
As a user, I would love to use software that uses up-to-date dependencies for security and UX benefits.
I'm trying to replace siwe
with ssx
on server side to interact with our API. This results in an error. The driver fields expects windows.ethereum and wraps it into Web3Provider internally which fails if you pass a normal provider. I'm not sure if there is a way to make this work in server side context without windows.ethereum.
Sample Code
const wallet = ethers.Wallet.createRandom();
const signer = wallet.connect(
new ethers.JsonRpcProvider("https://eth.rpc.blxrbdn.com")
);
const ssx = new SSX({
resolveEns: false,
enableDaoLogin: false,
providers: {
web3: { driver: signer.provider },
},
});
const session = await ssx.signIn();
Allow ability to pass a different signer provider in server side context or add a new method to receive siwe message for signing using the choice of your own signer. This will allow people to remove siwe and move to ssx which seems to be more well maintained going forward.
No response
No response
https://www.sprucekit.dev/ssx/quickstart/implementing-the-userauthorization-module
I followed the above example and received the error when I pressed the "Sign-In With Ethereum" button. It was confirmed that /api/ssx-nonce api received a request and returned a response, but it appears that wasm had a problem accessing memory when setting up ssx.
There appears to be a problem with the wasm file in the ssx library!
const ssxHandler = async () => { // here! const ssx = new SSX({ providers: { server: { host: "http://localhost:3000/api", }, }, }); console.log(ssx); await ssx.signIn(); console.log("here?"); setSSX(ssx); };
No response
No response
I'm using the ssx-server-dev
container described in the <repo>/docker-compose.yml
to attempt to do local development. I've removed the front-end in order to use my own from the docker-compose.yml
, so my file looks like:
services:
ssx-server:
container_name: ssx-server-dev
image: spruce/ssx-server-dev
env_file: .env
build:
context: .
dockerfile: ssx-server.Dockerfile
ports:
- "8443:8443"
The container works for the first log in. The container outputs the following log:
ssx-server-dev | {
ssx-server-dev | userId: 'did:pkh:eip155:1:0xdA3176d77c04632F2862B14E35bc6B4717FB5016',
ssx-server-dev | type: 'ssx-login',
ssx-server-dev | content: {
ssx-server-dev | signature: '0xf977aaedd14c0ee9121edce880fd58132cdf580a6eb1966623f3c2b8d66a77e423dec09db2b9b32149bd79e11a99eb8310f3a5db1d541c6b7bd74f1515c6332d1c',
ssx-server-dev | siwe: 'localhost wants you to sign in with your Ethereum account:\n' +
ssx-server-dev | '0xdA3176d77c04632F2862B14E35bc6B4717FB5016\n' +
ssx-server-dev | '\n' +
ssx-server-dev | '\n' +
ssx-server-dev | 'URI: did:key:z6MkpjgcdvifNmcj5gCz39QSVegUM4mzKG8rMTox8vGnJDWY#z6MkpjgcdvifNmcj5gCz39QSVegUM4mzKG8rMTox8vGnJDWY\n' +
ssx-server-dev | 'Version: 1\n' +
ssx-server-dev | 'Chain ID: 1\n' +
ssx-server-dev | 'Nonce: TtC8dYMIK8rzTT6tW\n' +
ssx-server-dev | 'Issued At: 2023-01-05T20:53:18.905Z',
ssx-server-dev | isGnosis: false
ssx-server-dev | },
ssx-server-dev | timestamp: '2023-01-05T20:53:21.728Z'
ssx-server-dev | }
If I refresh the client app, then log in again, it crashes with the message:
ssx-server-dev | /root/ssx/packages/ssx-server/dist/middlewares/express/middleware.js:59
ssx-server-dev | const { success: verified, data } = siweMessageVerify;
ssx-server-dev | ^
ssx-server-dev |
ssx-server-dev | TypeError: Cannot destructure property 'success' of 'siweMessageVerify' as it is undefined.
ssx-server-dev | at /root/ssx/packages/ssx-server/dist/middlewares/express/middleware.js:59:30
ssx-server-dev | at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
ssx-server-dev |
ssx-server-dev | Node.js v18.12.1
ssx-server-dev exited with code 1
This is consistently reproducible.
The ssx-server-dev container can handle a second user logging in or a malformed message without crashing.
One major issue is that de-structuring an undefined
property in JavaScript causes an unrecoverable error and program wide crash. De-structuring without validation is always going to risk making a recoverable error into a fatal one.
I think we should strive to make them recoverable, in the sense that one malformed message from one user doesn't crash the whole system for other users. That way a useful error can be returned in the case of accidental malformed messages and the system is not vulnerable to one bad message being enough to launch a DoS attack.
After SXX signIn
method is called, a gray modal flashes in the screen and before the /ssx-nonce
http call.
Due to how fast the modal show us and disappear on the screen, I wasn't able to screenshot it.
As I was trying to screen shot the modal, I tried to apply network throttling, and notice that on really slow connections the modal doesn't show up.
The network throttling specs used:
Download: 100 kbit/s
Upload: 10 kbit/s
Latency: 10000 ms
It also doesn't show up when a debugger
is placed right before signIn
method being called, and we walk through every step of the debugger.
Not displaying any UI element during the sign in process
There's only one step that's calling the sign-in method. On my app, I'm doing as follows:
import { PUBLIC_EXODA_API } from '$env/static/public'
import { SSX } from "@spruceid/ssx";
/**
* Interacts with Exoda's API to handle authentication process
*/
export class AuthService {
/**
* Uses SSX to create a session between
*
* @return user address or throws
*/
public static async auth(): Promise<string> {
const { address } = await AuthService._ssx.signIn()
if (!address)
throw new Error('AuthService::auth: address no present on response from the server')
return address
}
private static get _ssx(): SSX {
return new SSX({
enableDaoLogin: true,
resolveEns: true,
providers: {
web3: {
driver: window.ethereum,
},
server: {
host: PUBLIC_EXODA_API,
routes: {
nonce: '/nonce',
login: '/login',
logout: '/logout',
}
},
},
})
}
}
No response
When connecting to a MetaMask wallet via rainbowkit/siwe, I am prompted to sign a message to validate my session. When I switch accounts from within the same MetaMask wallet, the address
from wagmi's useAccount()
hook is updated, but the jwt associated with the session from the useSession()
next-auth/react hook is tied to my previous address.
I would like the session to be associated with the new account, even if it means signing a new signature. Right now I am manually flushing the session and programmatically logging in in a React useEffect()
.
No response
Currently there's no way to execute arbitrary code during the authentication process on the server.
This could be very handy for applications that use some kind of SQL database and needs to create a record their users, so they can later create relationships between the user and the rest of models on application.
One way to handle the execution of user defined functions on the server, would be adding a type to SSXServerRoutes
union types such as:
export interface SSXServerRoutes {
//...
login?: Partial<SSXRouteConfig> |
AxiosRequestConfig |
string |
{ path: string, hook: (req: Request) => Promise<void> };
//...
}
hook
must receive a Request
as parameters, so the function can check internally if the user is authenticated and decide which path to take. It'll return a Promise<void>
in order to enable async hooks, but to make sure the function doesn't return anything, since it wouldn't be used by ssx during the authentication.
the hook
would be called right before the response to the client, to make sure it doesn't interfere on the other stuff the route is doing.
I'd say yes, otherwise the client wouldn't know that something went wrong during the login, for example
/ssx-login
route?Yes, if the function shouldn't be executed when the auth fails, req.ssx.verified
should be used decide whether to continue or not.
app.use(
SSXExpressMiddleware(
ssx,
{
login: {
route: '/login',
hook: async (request: Request) => {
if (!req.ssx.verified)
return
const user = await userService.get(req.ssx.siwe.address)
if (!user)
await userService.create(req.ssx.siwe.address)
}
},
}
)
)
As a developers I'd like to be able to create a record on the database if it's the first time a user is signing in on my app
yarn dev
results in an error:
6:58:59 PM - Starting compilation in watch mode...
[0]
[1] /Users/art/Code/OSS/ssx/node_modules/ts-node/src/index.ts:859
[1] return new TSError(diagnosticText, diagnosticCodes, diagnostics);
[1] ^
[1] TSError: ⨯ Unable to compile TypeScript:
[1] src/index.ts(5,93): error TS2307: Cannot find module '@spruceid/ssx-server' or its corresponding type declarations.
[1] src/index.ts(39,12): error TS2339: Property 'ssx' does not exist on type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>'.
[1] src/index.ts(43,42): error TS2339: Property 'ssx' does not exist on type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>'.
[1] src/index.ts(47,17): error TS2339: Property 'ssx' does not exist on type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>'.
[1]
[1] at createTSError (/Users/art/Code/OSS/ssx/node_modules/ts-node/src/index.ts:859:12)
[1] at reportTSError (/Users/art/Code/OSS/ssx/node_modules/ts-node/src/index.ts:863:19)
[1] at getOutput (/Users/art/Code/OSS/ssx/node_modules/ts-node/src/index.ts:1077:36)
[1] at Object.compile (/Users/art/Code/OSS/ssx/node_modules/ts-node/src/index.ts:1433:41)
[1] at Module.m._compile (/Users/art/Code/OSS/ssx/node_modules/ts-node/src/index.ts:1617:30)
[1] at Module._extensions..js (node:internal/modules/cjs/loader:1213:10)
[1] at Object.require.extensions.<computed> [as .ts] (/Users/art/Code/OSS/ssx/node_modules/ts-node/src/index.ts:1621:12)
[1] at Module.load (node:internal/modules/cjs/loader:1037:32)
[1] at Function.Module._load (node:internal/modules/cjs/loader:878:12)
[1] at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:82:12) {
[1] diagnosticCodes: [ 2307, 2339, 2339, 2339 ]
[1] }
[1] [nodemon] app crashed - waiting for file changes before starting...
[0] src/index.ts(5,93): error TS2307: Cannot find module '@spruceid/ssx-server' or its corresponding type declarations.
[0] src/index.ts(39,12): error TS2339: Property 'ssx' does not exist on type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>'.
[0] src/index.ts(43,42): error TS2339: Property 'ssx' does not exist on type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>'.
[0] src/index.ts(47,17): error TS2339: Property 'ssx' does not exist on type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>'.
[0] src/redis.ts(5,93): error TS2307: Cannot find module '@spruceid/ssx-server' or its corresponding type declarations.
[0] src/redis.ts(41,15): error TS7006: Parameter 'session' implicitly has an 'any' type.
[0] src/redis.ts(63,12): error TS2339: Property 'ssx' does not exist on type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>'.
[0] src/redis.ts(67,42): error TS2339: Property 'ssx' does not exist on type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>'.
[0] src/redis.ts(71,17): error TS2339: Property 'ssx' does not exist on type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>'.
[0]
[0] 6:59:00 PM - Found 9 errors. Watching for file changes.
Server is expected to start
Env
Node version: v19.0.1
OS: Darwin Kernel Version 22.1.0: root:xnu-8792.41.9~2/RELEASE_ARM64_T6000
git clone https://github.com/spruceid/ssx.git
cd ssx/examples/ssx-test-express-api
yarn
yard dev
No response
In NextJS/NextAuth, when calling ssx?.signIn()
or ssx?.signOut()
, the following error occurs in server logs:
[next-auth][error][CLIENT_FETCH_ERROR]
https://next-auth.js.org/errors#client_fetch_error Unexpected token E in JSON at position 0 {
error: {
message: 'Unexpected token E in JSON at position 0',
stack: 'SyntaxError: Unexpected token E in JSON at position 0\n' +
' at JSON.parse (<anonymous>)\n' +
' at parseJSONFromBytes (/home/ben/Documents/learn/new/guide-t3-ssx-minimal/node_modules/next/dist/compiled/undici/index.js:2:4905)\n' +
' at successSteps (/home/ben/Documents/learn/new/guide-t3-ssx-minimal/node_modules/next/dist/compiled/undici/index.js:2:4473)\n' +
' at /home/ben/Documents/learn/new/guide-t3-ssx-minimal/node_modules/next/dist/compiled/undici/index.js:2:65581\n' +
' at node:internal/process/task_queues:141:7\n' +
' at AsyncResource.runInAsyncScope (node:async_hooks:203:9)\n' +
' at AsyncResource.runMicrotask (node:internal/process/task_queues:138:8)\n' +
' at processTicksAndRejections (node:internal/process/task_queues:96:5)',
name: 'SyntaxError'
},
url: 'http://localhost:3000/api/auth/csrf',
message: 'Unexpected token E in JSON at position 0'
}
No error occurs when calling singIn/singOut
Minimum reproducible repo: https://github.com/junhuang-ho/test-t3-ssx
in pages/index.tsx
, after connecting, try the signIn/signOut buttons
No response
Using express-api
example for local development, running the command:
$ yarn express-api serve
Occasionally, the log in attempt fails with the same Cannot read properties of undefined (reading 'nonce')
message that was in #54
This appears to be the same bug, but no longer reliablely reproducible. I've noticed it ~ 1 in every 10 logins, and @anukritidata ran into it once.
The changes in recent PRs seem to have altered the failure from every other login to much rarer but still present, and the changes may have a clue as to where the problem lies.
No failures from an undefined nonce.
On the main branch, using the express-api
example, every once in a while, the nonce will be undefined.
I have a feeling this will be a tricky one. It may be worth going back in time to PR #64 or earlier when this happened consistently.
According to SSX docs, the only authentication method provided between the client and the server are session cookies.
Adding JWT authentication would allow users handle the session scalability problem easily by sharing the JWT secret key between servers. Once a JWT is emitted by one server, all the other ones can verify the validity of the token, by providing the verify
function with the secret key and the token passed from the client.
As SSX doesn't rely on a database to store the current nonce of a given user, maybe JWT authentication would still need the use of ssx-nonce cookie to receive the nonce and verify the signature provided by the client. After the JWT is passed by the client, the nonce cookie can be destroyed.
In my opinion, ssx-nonce cookie is still required because that's the only way to guarantee that the nonce was emitted by the same server.
A constraint implied by this method is that the whole authentication process ('/nonce', '/login') would need to happen in a single server until the server responds to the client with a JWT, since it's cookie dependent. But I don't see a reason why a authentication process would happen on two servers.
This method also allows the creation of an "authentication service" package, which would be a wrapper around express that provides the three endpoints, (nonce, login and logout) and the user would only need to pass the secret key for signing the JWT. The rest of the ecosystem would simply verify the JWT using the same secret provided by this "authentication service" package.
As a web developer I would like to be able to implement JWT authentication using SSX because it allows to scale the server in a simpler form.
When performing a signOut()
with custom routes set, the custom /login
route is hit.
Snippet:
import { SSX } from "@spruceid/ssx";
// ...
function createSSX() {
return new SSX({
providers: {
server: {
host: "http://localhost:3001",
routes: {
login: "/login",
logout: "/logout",
nonce: "/nonce",
},
},
}
})
}
// ...
const ssxLogoutHandler = async () => {
await ssxProvider?.signOut();
setSSX(null);
window.ssx = null;
};
The custom route /logout
, should be used.
"@spruceid/ssx"
package..signOut()
methodThink it is related to this:
https://github.com/spruceid/ssx/blob/main/packages/ssx-sdk/src/core.ts#L328
Where we need to change the route to be logout
instead of login
- const route = this.config.providers?.server?.routes?.login ?? '/ssx-logout';
+ const route = this.config.providers?.server?.routes?.logout ?? '/ssx-logout';
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.