Skip to content

Commit

Permalink
feat(h3-firebase-auth): init h3 firebase auth middleware (#4)
Browse files Browse the repository at this point in the history
* fix(lint): unify rome check typescript

* test: add h3 firebase test cases

* feat(h3): init h3 firebase auth middleware

* bump: release all

* fix(auth): update typing of decoded firebase id token

* chore: revert another packages to head

* fix(h3-firebase-auth): update typo
  • Loading branch information
harrytran998 authored Aug 16, 2023
1 parent 0889079 commit 6fd7877
Show file tree
Hide file tree
Showing 13 changed files with 1,327 additions and 475 deletions.
11 changes: 9 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
{
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports.rome": true,
"quickfix.rome": true
},
"editor.rulers": [100],
"editor.defaultFormatter": "rome.rome",
"cSpell.words": [
"fiboup"
]
"edgelibs",
"fiboup",
"hono"
],

}
16 changes: 9 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,24 @@
"name": "@fiboup/edgelibs",
"version": "1.0.0",
"private": true,
"workspaces": [
"packages/**"
],
"workspaces": ["packages/**"],
"scripts": {
"build": "pnpm -r --parallel run build",
"build:firebase-auth": "pnpm -r --filter=./packages/firebase-auth run build",
"build:hono-firebase-auth": "pnpm -r --filter=./packages/hono-firebase-auth run build"
"build:hono-firebase-auth": "pnpm -r --filter=./packages/hono-firebase-auth run build",
"lint.format": "rome check --apply-unsafe packages/**/*.ts packages/**/**/*.ts",
"lint.check": "rome check packages/**/*.ts packages/**/**/*.ts"
},
"keywords": [],
"author": "Hieu Tran <[email protected]>",
"license": "MIT",
"devDependencies": {
"@changesets/cli": "^2.26.2",
"eslint": "^8.39.0",
"@types/supertest": "2.0.12",
"rome": "12.1.3",
"typescript": "^5.1.5",
"unbuild": "^1.2.1"
"typescript": "^5.1.6",
"unbuild": "^1.2.1",
"vitest": "0.34.1",
"supertest": "6.3.3"
}
}
12 changes: 12 additions & 0 deletions packages/h3-firebase-auth/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# @fiboup/h3-firebase-auth

## 1.0.0

### Major Changes

- Init h3 firebase auth middleware

### Patch Changes

- Updated dependencies
- @fiboup/firebase-auth@1.1.1
96 changes: 96 additions & 0 deletions packages/h3-firebase-auth/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Firebase Authentication/Identity Platform integration for h3

This package allows easily integrate Firebase Authentication/Identity Platform to your [h3](https://github.com/unjs/h3) API project.

## Features

- h3 middleware to decode and verify JWT token issued by Firebase Authentication service
- h3 context variable `user` for you to access any where in your application

```ts
const user = event.context.user // DecodedIdToken;
```

- Allowed to custom `user` shape with `transformCurrentUser`


## Install

With NPM

```bash
npm install @fiboup/h3-firebase-auth
```

With Yarn

```bash
yarn add @fiboup/h3-firebase-auth
```

With pnpm

```bash
pnpm add @fiboup/h3-firebase-auth
```

## Usage

### Add authentication middleware to your h3 app

```ts
import { createApp } from "h3";
import { validateFirebaseAuth } from "@fiboup/h3-firebase-auth";

const app = new createApp();

app.use(
"/",
eventHandler({
onRequest: [
(event) =>
validateFirebaseAuth({
// Note: Go to your Firebase project, then visit Project settings for the project id***
projectId: "TEST",
// If you ignore this field, default is `user`
userContextKey: "currentUser",
transformCurrentUser(user) {
return {
...user,
nickname: user.email?.split("@")[0],
};
},
})(event),
],
async handler(event) {
const user = event.context.currentUser;
return `Hello ${user.email}`;
},
}),
)
```

To make your custom current user context type-safe, don't forget to write your custom h3 in H3EventContext
on global typing of project, like `global.d.ts`


```ts
declare module "h3" {
interface H3EventContext extends Record<string, any> {
params?: Record<string, string>;
/**
* Matched router Node
*
* @experimental The object structure may change in non-major version.
*/
matchedRoute?: RouteNode;
sessions?: Record<string, Session>;
clientAddress?: string;
user?: DecodedIdToken;
}
}
```

## License

MIT &copy; [Fiboup](https://github.com/fiboup)
10 changes: 10 additions & 0 deletions packages/h3-firebase-auth/build.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { defineBuildConfig } from "unbuild";

export default defineBuildConfig({
entries: ["./src/index"],
rollup: {
emitCJS: true,
},
declaration: true,
clean: true,
});
59 changes: 59 additions & 0 deletions packages/h3-firebase-auth/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
{
"name": "@fiboup/h3-firebase-auth",
"author": "Harry Tran <[email protected]>",
"license": "MIT",
"version": "1.0.0",
"description": "Firebase Authentication for H3",
"type": "module",
"repository": {
"type": "git",
"url": "git+https://github.com/fiboup/edgelibs.git",
"directory": "packages/h3-firebase-auth"
},
"bugs": {
"url": "https://github.com/fiboup/edgelibs/issues"
},
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
}
},
"main": "./dist/index.cjs",
"types": "./dist/index.d.ts",
"typesVersions": {
"*": {
"*": [
"*",
"dist/*",
"dist/*.d.ts",
"dist/*/index.d.ts"
]
}
},
"files": [
"dist"
],
"scripts": {
"build": "unbuild",
"test": "vitest"
},
"devDependencies": {
"h3": "^1.8.0"
},
"peerDependencies": {
"h3": "^1.8.0"
},
"dependencies": {
"@fiboup/firebase-auth": "workspace:*"
},
"keywords": [
"firebase",
"auth",
"cloud identity",
"identity platform",
"CIAM",
"h3"
]
}
3 changes: 3 additions & 0 deletions packages/h3-firebase-auth/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { JwtDecodeError } from "@fiboup/firebase-auth";

export * from "./middleware";
55 changes: 55 additions & 0 deletions packages/h3-firebase-auth/src/middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import type { DecodedIdToken } from "@fiboup/firebase-auth";
import { fetchGooglePublicKeys, verifyAndDecodeJwt } from "@fiboup/firebase-auth";
import { createError, defineRequestMiddleware, getHeader } from "h3";

const TOKEN_PREFIX = "Bearer ";
const USER_CONTEXT_KEY = "user";

export type FirebaseAuthConfig = {
projectId: string;
transformCurrentUser?: (decodedToken: DecodedIdToken) => unknown;
/**
* If you leave not set this value, the default value is `user`.
* @default user
*/
userContextKey?: string;
};

export type DefaultFirebaseAuthInjectedVariables = {
currentUser?: DecodedIdToken;
};

export const transformCurrentUser = (decodedToken: DecodedIdToken) => {
return decodedToken;
};

export const validateFirebaseAuth = (config: FirebaseAuthConfig) => {
return defineRequestMiddleware(async (event) => {
if (!config.projectId) {
throw createError({
statusCode: 400,
statusMessage: "Bad Request: Must provide projectId config",
});
}

const tokenHeader = getHeader(event, "Authorization") || "";
if (!tokenHeader) {
throw createError({ statusCode: 401, statusMessage: "Authorization header is missing" });
}
if (!tokenHeader.startsWith(TOKEN_PREFIX)) {
throw createError({
statusCode: 401,
statusMessage: `Authorization header must start with ${TOKEN_PREFIX}`,
});
}

const token = tokenHeader.substring(TOKEN_PREFIX.length);
const googlePublicKeys = await fetchGooglePublicKeys();
const decodedToken = await verifyAndDecodeJwt(token, googlePublicKeys, config.projectId);
const _transformCurrentUser = config?.transformCurrentUser || transformCurrentUser;
const user = _transformCurrentUser(decodedToken);
event.context[config.userContextKey ?? USER_CONTEXT_KEY] = user;
});
};

export type { DecodedIdToken };
Loading

0 comments on commit 6fd7877

Please sign in to comment.