Deno KV OAuth (Beta)
Features
- Uses oauth2_client for OAuth 2.0 workflows and Deno KV for persistent session storage.
- Automatically handles the authorization code flow with Proof Key for Code Exchange (PKCE), access token refresh, and client redirection.
- Comes with pre-configured OAuth 2.0 clients for popular providers.
- Works locally and in the cloud, including Deno Deploy.
- Based on the
Request
andResponse
interfaces from the Web API. - Works with
std/http's
serve()
andDeno.serve()
native HTTP servers, and web frameworks such as Fresh and Oak. See the In the Wild section below for examples and demos.
Live Demo
You can also check out a live demo at https://kv-oauth.deno.dev, which uses Github as the OAuth 2.0 provider. Source code is located in demo.ts.
Usage
Check out the full documentation and API reference here.
Fresh
Getting Started withNote: The minimum required version for plugins in Fresh is 1.3.0
If you're not performing anything special in the sign-in, sign-out and callback
handlers, you can add the Fresh plugin to your project. This automatically
handles GET /oauth/signin
, GET /oauth/callback
and GET /oauth/signout
routes.
-
Create your OAuth 2.0 application for your given provider.
-
Create your pre-configured or custom OAuth 2.0 client instance and configure Fresh to use the plugin.
// main.ts import { start } from "$fresh/server.ts"; import { createGitHubOAuth2Client } from "https://deno.land/x/deno_kv_oauth@$VERSION/mod.ts"; import { kvOAuthPlugin } from "https://deno.land/x/deno_kv_oauth@$VERSION/fresh.ts"; import manifest from "./fresh.gen.ts"; await start(manifest, { plugins: [ kvOAuthPlugin(createGitHubOAuth2Client()), ], });
If you require more advanced setups, you can create your own plugin. For more information, see:
- The source code for
kvOAuthPlugin()
- The Plugin documentation for Fresh
- The Fresh + Deno KV OAuth demo which uses the Fresh plugin
- Deno SaaSKit's custom plugin implementation
Getting Started with Other Frameworks
This example uses GitHub as the OAuth 2.0 provider. However, you can use any provider you like.
-
Create your OAuth 2.0 application for your given provider.
-
Create your pre-configured or custom OAuth 2.0 client instance.
// Pre-configured OAuth 2.0 client import { createGitHubOAuth2Client } from "https://deno.land/x/deno_kv_oauth@$VERSION/mod.ts"; const oauth2Client = createGitHubOAuth2Client();
-
Using the OAuth 2.0 client instance, insert the authentication flow functions into your authentication routes.
// Sign-in, callback and sign-out handlers import { createGitHubOAuth2Client, handleCallback, signIn, signOut, } from "https://deno.land/x/deno_kv_oauth@$VERSION/mod.ts"; const oauth2Client = createGitHubOAuth2Client(); async function handleSignIn(request: Request) { return await signIn(request, oauth2Client); } async function handleOAuth2Callback(request: Request) { return await handleCallback(request, oauth2Client); } async function handleSignOut(request: Request) { return await signOut(request); }
-
Use Deno KV OAuth's helper functions where needed.
// Protected route import { createGitHubOAuth2Client, getSessionAccessToken, getSessionId, } from "https://deno.land/x/deno_kv_oauth@$VERSION/mod.ts"; const oauth2Client = createGitHubOAuth2Client(); async function getGitHubUser(accessToken: string): Promise<any> { const response = await fetch("https://api.github.com/user", { headers: { authorization: `Bearer ${accessToken}` }, }); if (!response.ok) { const { message } = await response.json(); throw new Error(message); } return await response.json(); } async function handleAccountPage(request: Request) { const sessionId = getSessionId(request); const hasSessionIdCookie = sessionId !== undefined; if (!hasSessionIdCookie) return new Response(null, { status: 404 }); const accessToken = await getSessionAccessToken(oauth2Client, sessionId); if (accessToken === null) return new Response(null, { status: 400 }); try { const githubUser = await getGitHubUser(accessToken); return Response.json(githubUser); } catch (error) { console.error(error); return Response.error(); } }
-
Start your server with the necessary environment variables.
GITHUB_CLIENT_ID=xxx GITHUB_CLIENT_SECRET=xxx deno run --unstable --allow-env --allow-net server.ts
Check out a full implementation in the demo source code.
-
When needed, you can delete all KV-stored OAuth 2.0 sessions and tokens.
import { clearOAuthSessionsAndTokens } from "https://deno.land/x/deno_kv_oauth@$VERSION/mod.ts"; await clearOAuthSessionsAndTokens();
Pre-configured OAuth 2.0 Clients
This module comes with a suite of pre-configured OAuth 2.0 clients for the following providers:
Each function is typed so that their respective platform's requirements are met.
If there's a pre-configured OAuth 2.0 client for a provider you'd like added, please submit a pull request or create a new issue.
Custom OAuth 2.0 Client
If you require custom OAuth 2.0 configuration, you must define your client
using
new OAuth2Client()
from the oauth2_client
module.
E.g.:
import { OAuth2Client } from "https://deno.land/x/oauth2_client/mod.ts";
const client = new OAuth2Client({
clientId: Deno.env.get("CUSTOM_CLIENT_ID")!,
clientSecret: Deno.env.get("CUSTOM_CLIENT_SECRET")!,
authorizationEndpointUri: "https://custom.com/oauth/authorize",
tokenUri: "https://custom.com/oauth/token",
redirectUri: "https://my-site.com",
});
Environment Variables
KV_PATH
(optional) - defines the path that Deno KV uses. See the API reference for further details.${PROVIDER}_CLIENT_ID
and${PROVIDER}_CLIENT_SECRET
- required when creating a pre-configured OAuth 2.0 client for a given provider. E.g. for Twitter, the environment variable keys areTWITTER_CLIENT_ID
andTWITTER_CLIENT_SECRET
. See the list below for specifics.OKTA_DOMAIN
orAUTH0_DOMAIN
- required only when using the Okta or Auth0 provider to supply your own given domain.
Note: reading environment variables requires the
--allow-env[=<VARIABLE_NAME>...]
permission flag. See the manual for further details.
Running the Demo
Run deno task demo
to start the demo application. The task uses environment
variables defined in a .env
file at the root of this folder.
By default, the demo uses GitHub with a minimal scope. Use the PROVIDER
and
SCOPE
environment variables, if you'd like to change this behavior. E.g. for
Twitter:
PROVIDER=Twitter SCOPE=users.read deno task demo
Redirect URL after Sign-In or Sign-Out
The URL that the client is redirected to upon successful sign-in or sign-out is determined by the request made to the sign-in or sign-out endpoint. This value is set by the following order of precedence:
- The value of the
success_url
URL parameter of the request URL, if defined. E.g. a request tohttp://example.com/signin?success_url=/success
redirects the client to/success
after successful sign-in. - The value of the
Referer
header, if of the same origin as the request. E.g. a request tohttp://example.com/signin
withReferer
headerhttp://example.com/about
redirects the client tohttp://example.com/about
after successful sign-in. - The root path, "/". E.g. a request to
http://example.com/signin
without theReferer
header redirects the client tohttp://example.com
after successful sign-in.
The same applies to user sign-out.
Known Issues
Twitch Incompatibility
This module is incompatible with Twitch as an OAuth 2.0 provider, as the platform doesn't support PKCE. PKCE is a requirement for all OAuth 2.0 providers for this module.
In the Wild
Check out these projects powered by Deno KV OAuth 2.0:
- Deno SaaSKit - A modern SaaS template built on Fresh.
- KV SketchBook - Dead simple sketchbook app.
- Fresh + Deno KV OAuth demo - A demo of Deno KV OAuth working in the Fresh web framework.
- Oak + Deno KV OAuth demo - A demo of Deno KV OAuth working in the Oak web framework.
- Ultra + Deno KV OAuth demo - A demo of Deno KV OAuth working in the Ultra web framework.
- Hono + Deno KV OAuth demo - A demo of Deno KV OAuth working in the Hono web framework.
- Cheetah + Deno KV OAuth demo - A demo of Deno KV OAuth working in the Cheetah web framework.
- Paquet - A web app shop
Do you have a project powered by Deno KV OAuth 2.0 that you'd like to share? Please submit a pull request adding that project to this list.