Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for Astro #1075

Open
davidmytton opened this issue Jul 3, 2024 · 6 comments
Open

Support for Astro #1075

davidmytton opened this issue Jul 3, 2024 · 6 comments

Comments

@davidmytton
Copy link
Contributor

Astro allows middleware and provides the request information in the context. Per the docs, this is a standard Request object.

If we try to pass this through to aj.protect there is a type error:

Argument of type 'Request' is not assignable to parameter of type 'ArcjetNodeRequest'.
  Types of property 'headers' are incompatible.
    Type 'Headers' is not assignable to type 'Record<string, string | string[] | undefined>'.
      Index signature for type 'string' is missing in type 'Headers'.ts(2345)
(property) AstroSharedContext<Record<string, any>, Params>.request: Request

Note that Astro supports several rendering modes, including static (where middleware doesn't make sense) as well as multiple runtime adapters. So we'll need to make it clear that we require server mode otherwise you get this warning:

15:09:15 [WARN] `Astro.request.headers` is unavailable in "static" output mode, and in prerendered pages 
within "hybrid" and "server" output modes. If you need access to request headers, make sure that `output` 
is configured as either `"server"` or `output: "hybrid"` in your config file, and that the page accessing the 
headers is rendered on-demand.
@blaine-arcjet
Copy link
Contributor

ArcjetNodeRequest are you trying to use @arcjet/node here? That's not going to be the right adapter as node doesn't work with Request

@blaine-arcjet
Copy link
Contributor

Astro supports several rendering modes, including static

The unfortunate thing about this is that it seems like Astro handles rendering modes transparently and provides a request object but only makes request.url available in static mode. This won't work for our SDK.

@davidmytton
Copy link
Contributor Author

ArcjetNodeRequest are you trying to use @arcjet/node here? That's not going to be the right adapter as node doesn't work with Request

Yes. It didn't seem right to use one of the other adapters, but using @arcjet/next works so long as you have server rendering mode enabled.

astro.config.mjs

import { defineConfig } from 'astro/config';

import vercel from "@astrojs/vercel/serverless";

// https://astro.build/config
export default defineConfig({
  output: "server",
  adapter: vercel()
});

src/middleware.ts

import { defineMiddleware } from "astro:middleware";
import arcjet, { shield } from "@arcjet/next";

const aj = arcjet({
    // Get your site key from https://app.arcjet.com
    // and set it as an environment variable rather than hard coding.
    key: "ajkey_01hm7kbgdaexeambd6w9fdpy8x",
    rules: [
      // Protect against common attacks with Arcjet Shield
      shield({
        mode: "DRY_RUN", // Change to "LIVE" to block requests
      }),
    ],
  });

// `context` and `next` are automatically typed
export const onRequest = defineMiddleware(async (context, next) => {
    const decision = await aj.protect(context.request);
    console.log(decision)

    return next();
});

Result:

 astro  v4.11.4 ready in 82 ms

┃ Local    http://localhost:4321/
┃ Network  use --host to expose

15:18:47 watching for file changes...
15:18:48 [200] / 18ms
15:18:52 [watch] src/middleware.ts
✦Aj WARN Using 127.0.0.1 as IP address in development mode
ArcjetAllowDecision {
  id: 'req_01j1wfcsycfmk8e3jct37rynnm',
  ttl: 0,
  results: [
    ArcjetRuleResult {
      ruleId: '',
      ttl: 0,
      state: 'RUN',
      conclusion: 'ALLOW',
      reason: [ArcjetShieldReason]
    }
  ],
  ip: ArcjetIpDetails {
    latitude: undefined,
    longitude: undefined,
    accuracyRadius: undefined,
    timezone: undefined,
    postalCode: undefined,
    city: undefined,
    region: undefined,
    country: undefined,
    countryName: undefined,
    continent: undefined,
    continentName: undefined,
    asn: undefined,
    asnName: undefined,
    asnDomain: undefined,
    asnType: undefined,
    asnCountry: undefined,
    service: undefined
  },
  conclusion: 'ALLOW',
  reason: ArcjetShieldReason { type: 'SHIELD', shieldTriggered: false }
}
15:18:52 [200] / 249ms

@blaine-arcjet
Copy link
Contributor

Yeah, I figured that next or another Request adapter would "work". However, I don't think it will work in production mode because there's no IP available in the Astro request. We'll need to investigate Astro.clientAddress to get an IP

@davidmytton
Copy link
Contributor Author

Right. This is also working with the Vercel adapter locally, but there are others like Cloudflare and Deno Deploy that will probably need #759 and #758.

@blaine-arcjet
Copy link
Contributor

There are others like Cloudflare and Deno Deploy that will probably need #759 and #758.

I hope not. This should just need an Astro adapter. The @arcjet/next package !== vercel adapter for astro. You are just getting it to work because Request is fairly standardized.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants