Skip to content

Commit

Permalink
Merge pull request #1 from ensdomains/initial-staging-branch
Browse files Browse the repository at this point in the history
Staging branch
  • Loading branch information
storywithoutend authored Aug 20, 2024
2 parents 1e82c44 + c124c09 commit 0447dd3
Show file tree
Hide file tree
Showing 12 changed files with 173 additions and 95 deletions.
47 changes: 47 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Deploy

on: [push]

jobs:
deploy:
runs-on: ubuntu-latest
name: Deploy
steps:
- uses: actions/checkout@v4

- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.1.20

- name: Install dependencies
run: bun install --frozen-lockfile

- name: Deploy to production
if: ${{ github.ref == 'refs/heads/main' }}
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: deploy --env production
environment: production
secrets: |
DENTITY_CLIENT_SECRET
DENTITY_CLIENT_ID
env:
DENTITY_CLIENT_ID: ${{ secrets.DENTITY_CLIENT_ID }}
DENTITY_CLIENT_SECRET: ${{ secrets.DENTITY_CLIENT_SECRET }}

- name: Deploy to staging
if: ${{ github.ref != 'refs/heads/main' }}
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: deploy --env staging
environment: staging
secrets: |
DENTITY_CLIENT_SECRET
DENTITY_CLIENT_ID
env:
DENTITY_CLIENT_ID: ${{ secrets.DENTITY_STAGING_CLIENT_ID }}
DENTITY_CLIENT_SECRET: ${{ secrets.DENTITY_STAGING_CLIENT_SECRET }}
Binary file modified bun.lockb
Binary file not shown.
19 changes: 11 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
{
"name": "router-ts-worker",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"deploy": "wrangler deploy",
"dev": "wrangler dev",
"test": "NODE_OPTIONS='--experimental-vm-modules --no-warnings' vitest run"
},
"devDependencies": {
"@biomejs/biome": "1.5.3",
"@cloudflare/workers-types": "^4.20240222.0",
"itty-router": "^4.0.27",
"miniflare": "^3.20240223.0",
"msw": "^2.3.4",
"typescript": "^5.3.3",
"vitest": "^1.3.1",
"vitest-environment-miniflare": "^2.14.2",
"wrangler": "^3.0.0"
},
"private": true,
"scripts": {
"deploy": "wrangler deploy",
"dev": "wrangler dev -e staging",
"test": "NODE_OPTIONS='--experimental-vm-modules --no-warnings' vitest run"
},
"type": "module",
"dependencies": {
"itty-router": "4.0.27"
}
}
8 changes: 4 additions & 4 deletions src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ test('v1 example handler for v1 example request', async () => {
{} as Env,
{} as ExecutionContext,
)
expect(v1.example).toHaveBeenCalled()
expect(v1.fetchDentityFederatedToken).toHaveBeenCalled()
expect(await response.text()).toBe('Hello, World!')
})

test('head handler for head request', async () => {
vi.mocked(v1.example).mockImplementation(() => {
vi.mocked(v1.fetchDentityFederatedToken).mockImplementation(() => {
const response = new Response('Hello, World!')
response.headers.set('Content-Type', 'application/json')
response.headers.set('Custom-Header', 'HeaderValue')
Expand All @@ -51,7 +51,7 @@ test('head handler for head request', async () => {
{} as Env,
{} as ExecutionContext,
)
expect(v1.example).toHaveBeenCalled()
expect(v1.fetchDentityFederatedToken).toHaveBeenCalled()
expect(response.status).toBe(200)
expect(response.body).toBe(null)
expect(response.headers.get('Content-Type')).toBe('application/json')
Expand Down Expand Up @@ -109,7 +109,7 @@ test('not found for unsupported path', async () => {
})

test('500 error+cors for internal error', async () => {
vi.mocked(v1.example).mockImplementation(() => {
vi.mocked(v1.fetchDentityFederatedToken).mockImplementation(() => {
throw new Error('test')
})
const response = await index.fetch(
Expand Down
11 changes: 6 additions & 5 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ const router = Router<IRequestStrict, [RouteParameters]>()
router.all('*', preflight)

// V1 Routes
router.get('/v1/example', v1.example)
router.head('/v1/example', v1.example)
router.options('/v1/example', () => new Response(null, { status: 204 }))
router.post('/v1/dentity/token', v1.fetchDentityFederatedToken)
router.head('/v1/dentity/token', v1.fetchDentityFederatedToken)
router.options('/v1/dentity/token', () => new Response(null, { status: 204 }))

// 404 Fallback
router.all('*', () => error(404, 'Not Found'))
Expand All @@ -30,9 +30,10 @@ export default {
router
.handle(request, { env, ctx })
.then(stripBodyForHeadRequest(request))
.catch((e) => {
console.error('Caught error')
.catch((e: unknown) => {
console.error(e)
const errorMsg = e instanceof Error ? e.message : ''
if (errorMsg) return error(400, errorMsg)
return error(500, 'Internal Server Error')
})
.then(corsify),
Expand Down
4 changes: 2 additions & 2 deletions src/routes/v1.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { example } from './v1/example.js'
import { fetchDentityFederatedToken } from './v1/dentity/fetchDentityFederatedToken.js'

export const v1 = {
example,
fetchDentityFederatedToken,
}
40 changes: 40 additions & 0 deletions src/routes/v1/dentity/fetchDentityFederatedToken.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { afterAll, afterEach, beforeAll, expect, test } from 'vitest'
import { fetchDentityFederatedToken } from './fetchDentityFederatedToken.js'
import { setupServer } from 'msw/node'
import { http, HttpResponse} from 'msw'

const restHandlers = [
http.post('https://example.com/oidc/token', () => {
return HttpResponse.json({
"access_token": "access-token",
"expires_in": 86400,
"id_token": "id-token",
"scope": "openid federated_token",
"token_type": "Bearer",
"federated_token": "federated-token",
"ens_name": "name.eth",
"eth_address": "0xaddress"
})
}),
]

const server = setupServer(...restHandlers)

beforeAll(() => server.listen({ onUnhandledRequest: 'error' }))

afterAll(() => server.close())

afterEach(() => server.resetHandlers())

test('works', async () => {
const request = new Request('http://localhost/v1/example', { body: JSON.stringify({ code: 'test-code' }), method: 'POST' })
const response = await fetchDentityFederatedToken(request, { env: {
DENTITY_CLIENT_ID: 'test-client-id',
DENTITY_CLIENT_SECRET: 'test-client-secret',
DENTITY_BASE_ENDPOINT: 'https://example.com',
APP_REDIRECT: 'https://example.com',
}, ctx: {} as ExecutionContext })
expect(response.status).toBe(200)
// expect(response.headers.get('Content-Type')).toMatchInlineSnapshot(`"application/json; charset=utf-8"`)
expect(await response.json()).toEqual({ name: 'name.eth', address: '0xaddress', token: 'federated-token' })
})
48 changes: 48 additions & 0 deletions src/routes/v1/dentity/fetchDentityFederatedToken.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { json } from "itty-router/json"

import type { RouteParameters } from "@/types.js"
import { error } from "itty-router"

type DentityFederatedTokenResponse = {
"access_token": string,
"expires_in": number,
"id_token": string,
"scope": "openid federated_token",
"token_type": "Bearer",
"federated_token": string,
"ens_name": string,
"eth_address": string
error?: string
error_description?: string
}

export const fetchDentityFederatedToken = async (
_request: Request,
{ env: _env, ctx: _ctx }: RouteParameters,
) => {
const { DENTITY_CLIENT_ID, DENTITY_CLIENT_SECRET, DENTITY_BASE_ENDPOINT, APP_REDIRECT } = _env
const { code } = (await _request.json()) as { code: string }

const body = new URLSearchParams()
body.append("client_id", DENTITY_CLIENT_ID)
body.append("client_secret", DENTITY_CLIENT_SECRET)
body.append("grant_type", "authorization_code")
body.append("code", code)
body.append("redirect_uri", APP_REDIRECT)

const resp = await fetch(`${DENTITY_BASE_ENDPOINT}/oidc/token`, { method: 'POST', body, headers: { 'Content-Type': 'application/x-www-form-urlencoded' } })
const data = await resp.json()

const { federated_token, ens_name, error: errorTitle, error_description, ...rest} = data as DentityFederatedTokenResponse

if (!federated_token || !ens_name) return error(400, {error : errorTitle, error_description})

const url = new URL(`${DENTITY_BASE_ENDPOINT}/oidc/vp-token`)
url.searchParams.append('federated_token', federated_token)
url.searchParams.append('ens_name', ens_name)

return json({
name: ens_name,
verifiedPresentationUri: url.toString(),
})
}
10 changes: 0 additions & 10 deletions src/routes/v1/example.test.ts

This file was deleted.

7 changes: 0 additions & 7 deletions src/routes/v1/example.ts

This file was deleted.

18 changes: 4 additions & 14 deletions worker-configuration.d.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,6 @@
interface Env {
// Example binding to KV. Learn more at https://developers.cloudflare.com/workers/runtime-apis/kv/
// MY_KV_NAMESPACE: KVNamespace;
//
// Example binding to Durable Object. Learn more at https://developers.cloudflare.com/workers/runtime-apis/durable-objects/
// MY_DURABLE_OBJECT: DurableObjectNamespace;
//
// Example binding to R2. Learn more at https://developers.cloudflare.com/workers/runtime-apis/r2/
// MY_BUCKET: R2Bucket;
//
// Example binding to a Service. Learn more at https://developers.cloudflare.com/workers/runtime-apis/service-bindings/
// MY_SERVICE: Fetcher;
//
// Example binding to a Queue. Learn more at https://developers.cloudflare.com/queues/javascript-apis/
// MY_QUEUE: Queue;
APP_REDIRECT: string;
DENTITY_CLIENT_ID: string;
DENTITY_CLIENT_SECRET: string;
DENTITY_BASE_ENDPOINT: string;
}
56 changes: 11 additions & 45 deletions wrangler.toml
Original file line number Diff line number Diff line change
@@ -1,51 +1,17 @@
name = "router-ts-worker"
name = "auth-worker"
main = "src/index.ts"
compatibility_date = "2024-02-23"

# Variable bindings. These are arbitrary, plaintext strings (similar to environment variables)
# Note: Use secrets to store sensitive data.
# Docs: https://developers.cloudflare.com/workers/platform/environment-variables
# [vars]
# MY_VARIABLE = "production_value"
[env.production]
name = "auth-worker"

# Bind a KV Namespace. Use KV as persistent storage for small key-value pairs.
# Docs: https://developers.cloudflare.com/workers/runtime-apis/kv
# [[kv_namespaces]]
# binding = "MY_KV_NAMESPACE"
# id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
[env.production.vars]
DENTITY_BASE_ENDPOINT = "https://oidc.dentity.com"
APP_REDIRECT = "https://ens.domains"

# Bind an R2 Bucket. Use R2 to store arbitrarily large blobs of data, such as files.
# Docs: https://developers.cloudflare.com/r2/api/workers/workers-api-usage/
# [[r2_buckets]]
# binding = "MY_BUCKET"
# bucket_name = "my-bucket"
[env.staging]
name = "auth-worker-staging"

# Bind a Queue producer. Use this binding to schedule an arbitrary task that may be processed later by a Queue consumer.
# Docs: https://developers.cloudflare.com/queues/get-started
# [[queues.producers]]
# binding = "MY_QUEUE"
# queue = "my-queue"

# Bind a Queue consumer. Queue Consumers can retrieve tasks scheduled by Producers to act on them.
# Docs: https://developers.cloudflare.com/queues/get-started
# [[queues.consumers]]
# queue = "my-queue"

# Bind another Worker service. Use this binding to call another Worker without network overhead.
# Docs: https://developers.cloudflare.com/workers/platform/services
# [[services]]
# binding = "MY_SERVICE"
# service = "my-service"

# Bind a Durable Object. Durable objects are a scale-to-zero compute primitive based on the actor model.
# Durable Objects can live for as long as needed. Use these when you need a long-running "server", such as in realtime apps.
# Docs: https://developers.cloudflare.com/workers/runtime-apis/durable-objects
# [[durable_objects.bindings]]
# name = "MY_DURABLE_OBJECT"
# class_name = "MyDurableObject"

# Durable Object migrations.
# Docs: https://developers.cloudflare.com/workers/learning/using-durable-objects#configure-durable-object-classes-with-migrations
# [[migrations]]
# tag = "v1"
# new_classes = ["MyDurableObject"]
[env.staging.vars]
DENTITY_BASE_ENDPOINT = "https://oidc.staging.dentity.com"
APP_REDIRECT = "https://dentity-integration.ens-app-v3.pages.dev"

0 comments on commit 0447dd3

Please sign in to comment.