Pollify is a full-stack polling platform where creators can build polls, collect responses, and view analytics in real time.
- Deployed App: https://lets-pollify.vercel.app/
- YouTube Demo: https://www.youtube.com/watch?v=6F8q_JEHs3I
It supports:
- Anonymous polls
- Authenticated polls
- Mixed mode (anonymous + authenticated)
- Live updates via WebSockets
- OIDC login with automatic refresh-token based session continuation
Pollify uses my own custom OpenID Connect-compatible auth provider:
- TokenShinobi OIDC Service:
https://token-shinobi.onrender.com/
Pollify uses Authorization Code + PKCE on the client side, then exchanges code on the backend and stores refresh tokens in secure HTTP-only cookies.
- Frontend: React + TypeScript + Vite
- Backend: Node.js + Express + TypeScript
- Database: MongoDB + Mongoose
- Realtime: Socket.IO
- Validation: Zod
- Authentication: OIDC (TokenShinobi), JWT verification via JWKS (
jose)
- Create multi-question polls.
- Each question supports:
- Single-choice answers
- 2 to 10 options
- Required/optional mode
- Poll-level settings:
- Response mode:
anonymous|authenticated|both - Expiry date/time
- Quick expiry pills:
5 min,15 min,30 min,1 hr,1 day,7 days
- Public shareable link per poll (
/poll/:slug) - One response per authenticated user per poll
- One response per anonymous browser session per poll
- Required-question validation before submit
- Poll expiry enforcement on backend
- Creator analytics dashboard
- Question-wise option counts and percentages
- Participation breakdown:
- Total responses
- Anonymous responses
- Authenticated responses
- Live analytics update through Socket.IO
- Creators can publish a poll to show final results.
- If poll is expired and user is creator, creator can still view results.
- Login through my own TokenShinobi OIDC service.
- Authorization Code exchange done server-side (
/api/auth/oidc/exchange). - Access token used in
Authorization: Bearer .... - Refresh token stored in HTTP-only cookie (
ps_refresh_token) scoped to/api/auth. - Automatic refresh using
/api/auth/refresh. - Supports both token shapes from OIDC responses:
accessToken/refreshTokenaccess_token/refresh_token- Silent session restore in the app (no forced login every 15 minutes).
- Redirects back to originating page after login (including public poll URLs).
- Route/page transition animations
- Smooth card/section entry transitions
- Mobile hamburger menu for navigation:
- Dashboard
- Create Poll
- Login/Logout
- Auth gate copy and flow improvements
Pollify/
client/ # React app
src/
api/ # Axios client + refresh interceptors
auth/ # OIDC + token helpers
pages/ # Route pages
ui/ # Shared UI components
server/ # Express API + sockets
src/
config/ # DB connection
controllers/ # Route handlers
middleware/ # Auth, error handling, async wrappers
models/ # Poll, Response, User schemas
routes/ # Auth + Poll routes
services/ # Analytics service
socket/ # Socket event wiringBase URL: ${VITE_API_URL}/api
Auth:
POST /auth/oidc/exchange- Exchange OIDC authorization code + PKCE verifierPOST /auth/refresh- Rotate access token using refresh cookiePOST /auth/logout- Clear refresh cookieGET /auth/me- Get current user profile
Polls:
POST /polls- Create poll (auth required)GET /polls/mine- List creator’s pollsGET /polls/:slug/analytics- Poll analytics (owner only)PATCH /polls/:slug/publish- Publish poll (owner only)GET /polls/public/:slug- Public poll detailsPOST /polls/public/:slug/respond- Submit public response
Health:
GET /health
Required:
PORT(default:8080)MONGO_URICLIENT_URL(comma-separated allowed origins, e.g.http://localhost:5173)OIDC_CLIENT_IDOIDC_CLIENT_SECRETOIDC_REDIRECT_URI(must match your client callback URL)OIDC_AUDIENCE(OIDC client identifier used when verifying access token)
Optional (has defaults in code):
OIDC_ISSUER_URL(default:https://token-shinobi.onrender.com)OIDC_JWKS_URI(default:${OIDC_ISSUER_URL}/certs)OIDC_TOKEN_ENDPOINT(default:https://token-shinobi.onrender.com/token)
VITE_API_URL(example:http://localhost:8080)VITE_SOCKET_URL(example:http://localhost:8080)VITE_OIDC_ISSUER(example:https://token-shinobi.onrender.com)VITE_OIDC_CLIENT_IDVITE_OIDC_REDIRECT_URI(example:http://localhost:5173/auth/callback)
git clone <your-repo-url>
cd Pollify
cd server && npm install
cd ../client && npm installCreate:
server/.envclient/.env
Use the variable list above.
cd server
npm run devServer runs on http://localhost:8080 by default.
cd client
npm run devClient runs on http://localhost:5173 by default.
npm run dev- Start Vite dev servernpm run build- Type-check and production buildnpm run preview- Preview production build
npm run dev- Start server in watch mode withtsxnpm run build- Compile TypeScriptnpm run start- Run compiled server fromdist
- Access tokens are not stored in cookies; refresh tokens are stored in HTTP-only cookies.
- Refresh cookie settings adapt for localhost vs production:
- Localhost:
sameSite=lax, non-secure - Production:
sameSite=none, secure - Rate limiting and helmet are enabled on backend.
- User signs in through TokenShinobi.
- Pollify exchanges auth code and stores refresh token cookie.
- User creates and shares poll.
- Responders submit answers based on poll response mode.
- Creator monitors dashboard analytics and can publish final results.
- Multi-select question type
- Scheduled publish/unpublish
- CSV export for responses
- Poll templates
- Team workspaces and role-based access
Built with care using my own custom OIDC stack powered by TokenShinobi:
https://token-shinobi.onrender.com/