Skip to content

Commit c822da0

Browse files
committed
🚧 test re-written server
1 parent bed7192 commit c822da0

File tree

10 files changed

+3952
-30
lines changed

10 files changed

+3952
-30
lines changed

.dockerignore

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,34 @@
1-
.github
2-
example
3-
target
1+
# Dependencies
2+
node_modules
3+
npm-debug.log
4+
yarn-debug.log
5+
yarn-error.log
6+
7+
# Environment
8+
.env
9+
.env.local
10+
.env.*.local
11+
12+
# IDE
13+
.idea
14+
.vscode
15+
*.swp
16+
*.swo
17+
18+
# Git
19+
.git
20+
.gitignore
21+
22+
# Docker
23+
Dockerfile
24+
docker-compose.yml
25+
.dockerignore
26+
27+
# Documentation
28+
README.md
29+
LICENSE
30+
31+
# Build output
32+
dist
33+
build
34+
*.log

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
**/target
22
/static
33
**/wrangler.toml
4-
**/node_module
4+
**/node_modules
55
**/dist

Dockerfile

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,6 @@
1-
FROM clux/muslrust:stable as chef
2-
WORKDIR /siwe-oidc
3-
RUN cargo install cargo-chef
4-
5-
FROM chef as dep_planner
6-
COPY ./src/ ./src/
7-
COPY ./Cargo.lock ./
8-
COPY ./Cargo.toml ./
9-
COPY ./siwe-oidc.toml ./
10-
RUN cargo chef prepare --recipe-path recipe.json
11-
12-
FROM chef as dep_cacher
13-
COPY --from=dep_planner /siwe-oidc/recipe.json recipe.json
14-
RUN cargo chef cook --release --recipe-path recipe.json
1+
FROM node:20-alpine as builder
152

16-
FROM node:18.20.0-alpine3.18 as node_builder
3+
WORKDIR /siwe-oidc
174

185
# Reference https://github.com/mhart/alpine-node/issues/27#issuecomment-880663905
196
RUN apk add --no-cache --virtual .build-deps alpine-sdk python3
@@ -24,27 +11,45 @@ ARG WALLET_CONNECT_ID
2411
ENV INFURA_ID=${INFURA_ID}
2512
ENV WALLET_CONNECT_ID=${WALLET_CONNECT_ID}
2613

14+
# Copy static files and UI
2715
ADD --chown=node:node ./static /siwe-oidc/static
2816
ADD --chown=node:node ./js/ui /siwe-oidc/js/ui
2917
WORKDIR /siwe-oidc/js/ui
3018
RUN yarn
3119
RUN yarn build
3220

33-
FROM chef as builder
34-
COPY --from=dep_cacher /siwe-oidc/target/ ./target/
35-
COPY --from=dep_cacher $CARGO_HOME $CARGO_HOME
36-
COPY --from=dep_planner /siwe-oidc/ ./
37-
RUN cargo build --release
21+
# Build the Node.js application
22+
FROM node:20-alpine
3823

39-
FROM alpine
40-
COPY --from=builder /siwe-oidc/target/x86_64-unknown-linux-musl/release/siwe-oidc /usr/local/bin/
4124
WORKDIR /siwe-oidc
42-
RUN mkdir -p ./static
43-
COPY --from=node_builder /siwe-oidc/static/ ./static/
44-
COPY --from=builder /siwe-oidc/siwe-oidc.toml ./
25+
COPY ./siwe-oidc-js ./siwe-oidc-js
26+
WORKDIR /siwe-oidc/siwe-oidc-js
27+
28+
# Install dependencies
29+
RUN npm install
30+
31+
# Copy application source
32+
COPY . .
33+
34+
# Copy static files from builder
35+
COPY --from=builder /siwe-oidc/static/ ./static/
36+
37+
# Generate RSA key if not provided
38+
RUN if [ -z "$RSA_PEM" ]; then \
39+
apk add --no-cache openssl && \
40+
openssl genrsa -out /tmp/private.pem 2048 && \
41+
export RSA_PEM=$(cat /tmp/private.pem) && \
42+
rm /tmp/private.pem; \
43+
fi
44+
45+
# Set environment variables
4546
ENV SIWEOIDC_ADDRESS="0.0.0.0"
47+
# Expose port
4648
EXPOSE 8000
47-
ENTRYPOINT ["siwe-oidc"]
49+
# Start the application
50+
CMD ["npm", "start"]
51+
52+
# Labels
4853
LABEL org.opencontainers.image.source https://github.com/spruceid/siwe-oidc
4954
LABEL org.opencontainers.image.description "OpenID Connect Identity Provider for Sign-In with Ethereum"
5055
LABEL org.opencontainers.image.licenses "MIT OR Apache-2.0"

siwe-oidc-js/package.json

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"name": "siwe-oidc-js",
3+
"version": "1.0.0",
4+
"description": "OpenID Connect provider with Sign-In with Ethereum support",
5+
"main": "src/index.js",
6+
"type": "module",
7+
"scripts": {
8+
"start": "node src/index.js",
9+
"dev": "nodemon src/index.js"
10+
},
11+
"dependencies": {
12+
"cookie-parser": "^1.4.6",
13+
"cors": "^2.8.5",
14+
"dotenv": "^16.3.1",
15+
"ethers": "^6.9.0",
16+
"express": "^4.18.2",
17+
"express-rate-limit": "^7.1.5",
18+
"express-validator": "^7.0.1",
19+
"helmet": "^7.1.0",
20+
"jsonwebtoken": "^9.0.2",
21+
"jwks-rsa": "^3.1.0",
22+
"node-rsa": "^1.1.1",
23+
"redis": "^4.6.11",
24+
"siwe": "^3.0.0",
25+
"viem": "2.28.1",
26+
"wagmi": "^2.15.1"
27+
},
28+
"devDependencies": {
29+
"nodemon": "^3.0.2"
30+
}
31+
}

siwe-oidc-js/src/config/index.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import dotenv from 'dotenv';
2+
import { URL } from 'url';
3+
4+
dotenv.config();
5+
6+
const config = {
7+
address: process.env.ADDRESS || '127.0.0.1',
8+
port: parseInt(process.env.PORT || '8000', 10),
9+
baseUrl: new URL(process.env.BASE_URL || 'http://127.0.0.1:8000'),
10+
rsaPem: process.env.RSA_PEM,
11+
redisUrl: new URL(process.env.REDIS_URL || 'redis://localhost'),
12+
defaultClients: {},
13+
requireSecret: process.env.REQUIRE_SECRET === 'true',
14+
ethProvider: process.env.ETH_PROVIDER ? new URL(process.env.ETH_PROVIDER) : null,
15+
16+
// OIDC Configuration
17+
signingAlg: 'RS256',
18+
kid: 'key1',
19+
scopes: ['openid', 'profile'],
20+
responseTypes: ['code', 'id_token', 'token id_token'],
21+
subjectIdentifierTypes: ['pairwise'],
22+
tokenEndpointAuthMethods: ['client_secret_basic', 'client_secret_post', 'private_key_jwt'],
23+
24+
// Paths
25+
metadataPath: '/.well-known/openid-configuration',
26+
jwkPath: '/jwk',
27+
tokenPath: '/token',
28+
authorizePath: '/authorize',
29+
registerPath: '/register',
30+
clientPath: '/client',
31+
userinfoPath: '/userinfo',
32+
signinPath: '/sign_in',
33+
siweCookieKey: 'siwe',
34+
touPath: '/legal/terms-of-use.pdf',
35+
ppPath: '/legal/privacy-policy.pdf'
36+
};
37+
38+
export default config;

siwe-oidc-js/src/db/redis.js

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { createClient } from 'redis';
2+
import config from '../config/index.js';
3+
4+
class RedisClient {
5+
constructor() {
6+
this.client = createClient({
7+
url: config.redisUrl.toString(),
8+
database: 2
9+
});
10+
11+
this.client.on('error', (err) => console.error('Redis Client Error', err));
12+
}
13+
14+
async connect() {
15+
await this.client.connect();
16+
}
17+
18+
async disconnect() {
19+
await this.client.disconnect();
20+
}
21+
22+
// Client management
23+
async getClient(clientId) {
24+
return await this.client.get(`client:${clientId}`);
25+
}
26+
27+
async setClient(clientId, clientData) {
28+
await this.client.set(`client:${clientId}`, JSON.stringify(clientData));
29+
}
30+
31+
async deleteClient(clientId) {
32+
await this.client.del(`client:${clientId}`);
33+
}
34+
35+
// Session management
36+
async getSession(sessionId) {
37+
return await this.client.get(`session:${sessionId}`);
38+
}
39+
40+
async setSession(sessionId, sessionData, ttl = 3600) {
41+
await this.client.set(`session:${sessionId}`, JSON.stringify(sessionData), {
42+
EX: ttl
43+
});
44+
}
45+
46+
async deleteSession(sessionId) {
47+
await this.client.del(`session:${sessionId}`);
48+
}
49+
50+
// Authorization code management
51+
async getAuthCode(code) {
52+
return await this.client.get(`auth_code:${code}`);
53+
}
54+
55+
async setAuthCode(code, codeData, ttl = 300) {
56+
await this.client.set(`auth_code:${code}`, JSON.stringify(codeData), {
57+
EX: ttl
58+
});
59+
}
60+
61+
async deleteAuthCode(code) {
62+
await this.client.del(`auth_code:${code}`);
63+
}
64+
65+
// Token management
66+
async getToken(token) {
67+
return await this.client.get(`token:${token}`);
68+
}
69+
70+
async setToken(token, tokenData, ttl = 3600) {
71+
await this.client.set(`token:${token}`, JSON.stringify(tokenData), {
72+
EX: ttl
73+
});
74+
}
75+
76+
async deleteToken(token) {
77+
await this.client.del(`token:${token}`);
78+
}
79+
}
80+
81+
export default new RedisClient();

siwe-oidc-js/src/index.js

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import express from 'express';
2+
import cors from 'cors';
3+
import helmet from 'helmet';
4+
import cookieParser from 'cookie-parser';
5+
import rateLimit from 'express-rate-limit';
6+
import config from './config/index.js';
7+
import db from './db/redis.js';
8+
import oidcRouter from './routes/oidc.js';
9+
10+
const app = express();
11+
12+
// Middleware
13+
app.use(helmet());
14+
app.use(cors({
15+
origin: true,
16+
credentials: true
17+
}));
18+
app.use(cookieParser());
19+
app.use(express.json());
20+
app.use(express.urlencoded({ extended: true }));
21+
22+
// Rate limiting
23+
const limiter = rateLimit({
24+
windowMs: 15 * 60 * 1000, // 15 minutes
25+
max: 100 // limit each IP to 100 requests per windowMs
26+
});
27+
app.use(limiter);
28+
29+
// Routes
30+
app.use('/', oidcRouter);
31+
32+
// Error handling
33+
app.use((err, req, res, next) => {
34+
console.error(err.stack);
35+
res.status(500).json({ error: 'Internal Server Error' });
36+
});
37+
38+
// Start server
39+
const startServer = async () => {
40+
try {
41+
await db.connect();
42+
43+
app.listen(config.port, () => {
44+
console.log(`Server running at http://${config.address}:${config.port}`);
45+
});
46+
} catch (error) {
47+
console.error('Failed to start server:', error);
48+
process.exit(1);
49+
}
50+
};
51+
52+
// Handle graceful shutdown
53+
process.on('SIGTERM', async () => {
54+
console.log('SIGTERM received. Shutting down gracefully...');
55+
await db.disconnect();
56+
process.exit(0);
57+
});
58+
59+
startServer();

0 commit comments

Comments
 (0)