Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/widavies/fireflare
Firebase authentication for Cloudflare workers
https://github.com/widavies/fireflare
authentication cloudflare cloudflare-workers firebase firebase-auth
Last synced: about 1 month ago
JSON representation
Firebase authentication for Cloudflare workers
- Host: GitHub
- URL: https://github.com/widavies/fireflare
- Owner: widavies
- License: apache-2.0
- Created: 2021-03-31T20:18:55.000Z (over 3 years ago)
- Default Branch: main
- Last Pushed: 2022-10-07T16:52:36.000Z (about 2 years ago)
- Last Synced: 2024-11-15T11:35:29.467Z (about 1 month ago)
- Topics: authentication, cloudflare, cloudflare-workers, firebase, firebase-auth
- Language: TypeScript
- Homepage:
- Size: 27.3 KB
- Stars: 16
- Watchers: 1
- Forks: 2
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# FireFlare
Firebase authentication for Cloudflare workers with no dependencies. Google public keys are cached with Workers KV to speed up authentication.# Installation
`npm i fireflare`https://www.npmjs.com/package/fireflare
# Usage
```typescript
import {auth} from "fireflare"// Taken from https://github.com/cloudflare/worker-template-router
async function handleRequest(request: Request) {
const r = new Router()r.get('/', async (request) => {
if(!(await auth('projectId', KV_NAMESPACE, request.headers.get('Authorization')?.replace("Bearer ", "") ?? null))) {
// Not authenticated
return new Response("Unauthorized");
}return new Response("Authorized");
}) // return a default message for the root routeconst resp = await r.route(request)
return resp
}
```
# Additional/custom claims validation
```typescript
if (!(await auth('projectId', env.KV_NAMESPACE, request.headers.get('Authorization')?.replace("Bearer ", "") ?? null, [
// Use a helper claims check function
Equals('custom-claim', 'expected-value'),
InPast('issued'),
InFuture('exp'),
NotEmpty('sub'),
// Validate claims with custom implementation
(claims) => {
const value = claims['custom-claim'];
// Return false to reject
return (typeof value === 'string' || value instanceof String) && value === "custom-value";
}
]))) {
// Not authenticated
return new Response("Unauthorized");
}
```# Example authentication using [itty-router](https://github.com/kwhitley/itty-router)
```typescript// Auth middleware
const requireUser = async (request: Request, env: Env) => {
const token = request.headers.get('Authorization')?.replace('Bearer ', '');return auth(env.FIREBASE_PROJECT_ID, env.KV_NAMESPACE, token ?? null).then((success) => {
if (!success) {
return new Response("Not authenticated", { status: 401 });
}
}).catch((err) => {
return new Response("Not authenticated", { status: 401 });
});
};router.get('/hello-auth', requireUser, async (request, env) => {
return new Response("Authenticated", { status: 200 });
});```
# Bonus: Full example (including CORS)
```typescript
export interface Env {
FIREBASE_PROJECT_ID: string,KV_NAMESPACE: KVNamespace;
}const CorsHeaders = {
"Access-Control-Allow-Origin": "https://www.example.com",
"Access-Control-Allow-Methods": "GET,HEAD,POST,OPTIONS,DELETE",
"Access-Control-Max-Age": "86400",
}export default {
async fetch (request: Request, env: Env, context: ExecutionContext) {
if (request.method === "OPTIONS") {
return handleOptions(request)
} else {
const response = await router.handle(request, env);if (response instanceof Response) {
for (const [key, value] of Object.entries(CorsHeaders)) {
response.headers.set(key, value);
}
}return response;
}
}
};const router = Router();
const requireUser = async (request: Request, env: Env) => {
const token = request.headers.get('Authorization')?.replace('Bearer ', '');return auth(env.FIREBASE_PROJECT_ID, env.KV_NAMESPACE, token ?? null).then((success) => {
if (!success) {
return new Response("Not authenticated", { status: 401 });
}
}).catch((err) => {
return new Response("Not authenticated", { status: 401 });
});
};router.get('/hello-auth', requireUser, async (request: IttyRequest, env: Env) => {
return new Response("Authenticated", { status: 200 });
});router.all('*', () => new Response('Not Found.', { status: 404 }));
// Cors header stuff
function handleOptions (request: Request) {
// Make sure the necessary headers are present
// for this to be a valid pre-flight request
let headers = request.headers
if (
headers.get("Origin") !== null &&
headers.get("Access-Control-Request-Method") !== null &&
headers.get("Access-Control-Request-Headers") !== null
) {
// Handle CORS pre-flight request.
// If you want to check or reject the requested method + headers
// you can do that here.
let respHeaders = {
...CorsHeaders,
// Allow all future content Request headers to go back to browser
// such as Authorization (Bearer) or X-Client-Name-Version
"Access-Control-Allow-Headers": request.headers.get("Access-Control-Request-Headers") as string,
}
return new Response(null, {
headers: respHeaders,
})
} else {
// Handle standard OPTIONS request.
// If you want to allow other HTTP Methods, you can do that here.
return new Response(null, {
headers: {
Allow: "GET, HEAD, POST, OPTIONS, DELETE",
},
})
}
}
```