pilcrowonpaper / oslo Goto Github PK
View Code? Open in Web Editor NEWA collection of auth-related utilities
Home Page: https://oslo.js.org
License: MIT License
A collection of auth-related utilities
Home Page: https://oslo.js.org
License: MIT License
In this link we have const qrcode = createQRCode(uri);
as placeholders. I had reviewed a few libraries to add an implemention to my project. I think this one is the most stable https://kazuhikoarase.github.io/qrcode-generator/js/demo/ and pure js. Using it is pretty straight forward I was wondering if I can add it as a function under oslo/otp
import { qrcode } from "$lib/qrcode"
var typeNumber = 0;
var errorCorrectionLevel = 'M';
var qr = qrcode(typeNumber, errorCorrectionLevel);
qr.addData(uri);
qr.make();
console.log(qr.createImgTag())
Using oslo/password
with the serverComponentsExternalPackages
option in Next.js causes errors when you try to import server actions from "use client"
components. Works fine when for server components. Looks to be the same issue as tursodatabase/libsql-client-ts#128.
Using esmExternals
does not seem to fix the issue, and Oslo doesn't use top-level await.
If you enable certificate authentication instead of token authentication in Microsoft Entra ID, you must authenticate via a Bearer token. (see https://learn.microsoft.com/en-us/answers/questions/346048/how-to-get-access-token-from-client-certificate-ca)
This is currently not supported by oslo.
I have already begun implementing this option and it seems like a trivial change (main...m-radmacher:oslo:feat/oauth-jwt-authentication).
If you're interested in merging this I would probably need to update the docs further.
The documentation says that it's recommended to use Argon2id or Scrypt instead of bcrypt if possible, but does not explain why. What reason is it not recommended?
According to the OAuth2 specification a call to the token endpoint with the refresh_token
grant type can optionally accept scopes. Oslos OAuth2 client implementation does not currently support sending scopes in its refreshAccessToken
function.
Relevant spec: https://datatracker.ietf.org/doc/html/rfc6749#section-6
I was trying out lucia
, arctic
and oslo
on a recent project and had a great experience using these libraries!
Wanting to replace the auth system in another project which currently uses Passport
(because it supports authentication via Steam) made me realize, that oslo
does not support OpenId, let alone old OpenId 2.0 Steam is using to this day.
I'd really appreciate support for OpenId / Steam auth. One reason being that almost all libraries implementing Steam authentication use an old openid package that hasn't been updated in years (except for some dependency updates). Another reason is that those packages, namely passport and passport-steam, are a bit outdated I feel and are not native TypeScript projects.
I'd be willing to try to put together a PR, but I've never really tinkered with auth at its core. Though, I have looked at packages implementing Steam auth and have a slight idea of how it works. However, replacing openid with something custom has me on edge because I haven't really understood how it works or why you'd need it - and I'd rather not code any vulnerabilities into a package.
Upon reviewing the oslo
module, I've found its features compelling and plan on integrating it into our production applications. However, I've noticed an inconsistency concerning the oslo/jwt
not completely adhering to the JWT spec.
According to Section 4.1.3 of the spec, the aud
claim should usually be an array of strings, although it can also be just a string.
Looking your library's source code, I noticed that it only supports string
as the type of audience
. Can you confirm if this was an intentional design decision?
If not, I am happy to contribute by making a PR to resolve this issue.
I have a Next.js 14 project set up with the pages router. But, as I am setting up Lucia (I'm following the Username and Password Tutorial to set it up. But, I keep getting the following error message.
./node_modules/.pnpm/@[email protected]/node_modules/@node-rs/argon2-darwin-x64/argon2.darwin-x64.node
Module parse failed: Unexpected character '�' (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
(Source code omitted for this binary file)
I'm using PNPM as the package manager but I don't think that should cause any problems.
Having trouble using this package in a NestJS app, I guess because NestJS is still stuck on CommonJS unfortunately. Instead of importing like other packages, I do this inline in code:
const { Argon2id } = await import('oslo/password');
const argon2id = new Argon2id();
const hash = await argon2id.hash(password);
However, I get the below error:
Error [ERR_REQUIRE_ESM]: require() of ES Module /node_modules/oslo/dist/password/index.js from /dist/api/auth/auth.service.js not supported.
Instead change the require of index.js in /dist/api/auth/auth.service.js to a dynamic import() which is available in all CommonJS modules.
That's what I'm doing above and tried many different variations. Anything else I can try or missing?
The example shown on https://oslo.js.org/reference/jwt/validateJWT is:
import { validateJWT } from "oslo/jwt";
try {
const jwt = validateJWT(
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtZXNzYWdlIjoiaGVsbG8ifQ.yP03DaEblJkk9mR-Y5L7YCMzJgHL-RDPx90aXz-cuAI"
);
const message = jwt.payload.message;
} catch {
// invalid signature
// expired token
// inactive token (`nbf`)
}
where a single string argument is passed to the validateJWT function.
However, the Definition in the docs is:
function validateJWT(
algorithm: JWTAlgorithm,
key: ArrayBuffer | TypedArray,
jwt: string
): Promise<JWT>;
which matches the code.
I'm interested in support for JWKS. To me that means:
In the past I have typically used https://mkjwk.org but I would much prefer a holistic solution from this great library. Thanks!
Auri is listed as dev dependency in lucia, but is a main dependency in both oslo and arctic. It is unused in prod code, and also installs 11 additional unused packages.
I'm trying to validate the authorization code with the client_secret
in the url
Looking at the code, it doesn't seem to be allowed, can you help me?
this is the expected url to validate the code
/v1/grant/?grant_type=authorization_code&client_id=[your_developer_id]&redirect_uri=[your_callback_url]&client_secret=[your_client_secret_code]&code=[your_authorization_code]
authenticateWith
only has 2 options: request_body
or http_basic_auth
(default)
probably it needs to have a third option to allow it in search params ?
I was reading the docs here for password functionality: https://oslo.js.org/reference/password/
It says, this requires Node APIs. If so, how would you hash a password in edge environment, like Cloudflare Workers or NextJs Edge/Middleware?
I've been having this issue for the passed few days now where i am logged out when i open my application the next morning.
I found out the the Expires / Max-Age
in the cookie is wrong even when i make sessionCookie expires: false
in lucia,
it is currently setting the Expires / Max-Age
as seconds, when converted to milliseconds i works as expected
Line 80 in c1f60ec
Currently I have an issue using the latest version of vercel adapter in my sveltekit app.
The message appears on prod build:
Warning: The following modules failed to locate dependencies that may (or may not) be required for your app to work: ...
I tested the node adapter and it seems to work fine with that.
Is there a way to fix it for vercel apps? I think vercel does not have all the node dependencies on their edge runtime: vercel blog
They suggest to use a different library such as bcrypt.js
First of all, thanks for this fantastic library! It would be great if one could only specify the .well-known discovery endpoint pointing to the server's metadata document (https://datatracker.ietf.org/doc/html/rfc8414) when instantiating the OAuth2Client and let it figure out the authorizeEndpoint and the tokenEndpoint automatically when sending requests.
Could working with signed cookies be made easier? For example maybe including a secrets parameter when creating and verifying cookies?
Support revocation of tokens via RFC7009
Hi,
while testing the library with entra id, I got the following error while trying to get the tokens:
AADSTS9002327: Tokens issued for the 'Single-Page Application' client-type may only be redeemed via cross-origin requests. Trace ID: <traceId> Correlation ID: <correlationId> Timestamp: 2024-03-22 09:42:30Z
Would it make sense the add a additional option to the validateAuthorizationCode
method to set the required headers?
My current workaround is to create my own token request.
Not sure if this is a problem with all providers, but based on what I have seen, it seems that EntraID needs the origin
header in the token request if we use a "public client".
Not sure if it helps, but here the code which I have used to test it.
( used hono via npm create hono@latest
and updated the src/index.ts
with the code below )
// required environment variables are:
// TENANT_ID and APP_ID
import { serve } from "@hono/node-server";
import { Hono } from "hono";
import { deleteCookie, getCookie, setCookie } from "hono/cookie";
import { MicrosoftEntraId, generateState, generateCodeVerifier } from "arctic";
import got from "got";
import { inspect } from "util";
const app = new Hono();
const redirectUri = "http://localhost:3000/api/auth/callback";
const entraId = new MicrosoftEntraId(
process.env.TENANT_ID,
process.env.APP_ID,
"",
redirectUri
);
app.get("/", async (c) => {
const state = generateState();
const codeVerifier = generateCodeVerifier();
const url = await entraId.createAuthorizationURL(state, codeVerifier, {
scopes: ["openid", "profile", "email", `${process.env.APP_ID}/.default`],
});
// store state verifier as cookie
setCookie(c, "state", state, {
secure: false,
path: "/",
httpOnly: true,
maxAge: 60 * 10, // 10 min
});
// store code verifier as cookie
setCookie(c, "code_verifier", codeVerifier, {
secure: false,
path: "/",
httpOnly: true,
maxAge: 60 * 10, // 10 min
});
return c.html(`
<div>
<a href="${url}">Get access token</a>
</div>
`);
});
app.get("/api/auth/callback", async (c) => {
const code = c.req.query("code");
const state = c.req.query("state");
const storedState = getCookie(c, "state");
const storedCodeVerifier = getCookie(c, "code_verifier");
if (!code || !storedState || !storedCodeVerifier || state !== storedState) {
// 400
throw new Error("Invalid request");
}
/**
* since we can't (yet) set the origin for the token request
* we have to use our own implementation to get the tokens
* implementation is based on https://github.com/pilcrowOnPaper/oslo/blob/main/src/oauth2/index.ts
*/
// const tokens = await entraId.validateAuthorizationCode(
// code,
// storedCodeVerifier
// );
const tokenResponse = await got(
`https://login.microsoftonline.com/${env.TENANT_ID}/oauth2/v2.0/token`,
{
method: "post",
form: {
code: code,
client_id: process.env.APP_ID,
grant_type: "authorization_code",
redirect_uri: redirectUri,
code_verifier: storedCodeVerifier,
},
responseType: "json",
headers: {
origin: redirectUri,
},
throwHttpErrors: false,
}
);
deleteCookie(c, "code");
deleteCookie(c, "state");
if (!tokenResponse.ok) {
console.log({ error: tokenResponse.body });
return c.text(
"Something went wrong while fetching the tokens ( check the console for more details )"
);
}
return c.text(inspect(tokenResponse.body, { depth: 2 }));
});
const port = 3000;
console.log(`Server is running on port ${port}`);
serve({
fetch: app.fetch,
port,
});
Currently, the createJWT
method only accepts the key as an ArrayBuffer
. This is probably because many methods in oslo/crypto only accept an ArrayBuffer
. However, the Web Crypto API often accepts TypedArray
(eg. Uint8Array
) and DataView
as well.
My personal use-case is that I am using JWTs in cookie sessions. Instead of generating the JWT key at runtime, I want to save the JWT key as a string, so the sessions are valid even when I restart the server.
In any case, ArrayBuffer
is harder to create/manipulate than Uint8Array
.
Because the Web Crypto API is compatible either way, this shouldn't require any changes in logic (just type-defs).
PS: Thanks so much for your work here and with lucia. It means I have one less thing to worry about when implementing authentication.
@node-rs/argon2
and @node-rs/bcrypt
are only required by the password provider and pull many huge dependencies (with binaries).
Is it possible to make it optional and reduce the number of dependencies ?
I get the following error in a SolidStart project (i.e. using Vite) after upgrading to [email protected]
:
[commonjs--resolver] Unexpected character '�' (Note that you need plugins to import files that are not JavaScript)
file: /Users/erikmuller/cn-webapp/node_modules/@node-rs/argon2/index.js:1:0
1: ����
���__TEXT@__text...
^
2: �H__LINKEDIT�
�/Users/runner/work/node-rs/node-rs/target/aarch6...
[12:36:09 AM] ERROR Unexpected character '�' (Note that you need plugins to import files that are not JavaScript)
at error (node_modules/vinxi/node_modules/vite/node_modules/rollup/dist/es/shared/node-entry.js:2287:30)
at Module.error (node_modules/vinxi/node_modules/vite/node_modules/rollup/dist/es/shared/node-entry.js:13745:16)
at Module.tryParse (node_modules/vinxi/node_modules/vite/node_modules/rollup/dist/es/shared/node-entry.js:14476:25)
at Module.setSource (node_modules/vinxi/node_modules/vite/node_modules/rollup/dist/es/shared/node-entry.js:14077:39)
at ModuleLoader.addModuleSource (node_modules/vinxi/node_modules/vite/node_modules/rollup/dist/es/shared/node-entry.js:24649:20)
The issue has also been reported for SvelteKit project here and the resolution proposed in this post doesn't work for me.
I'm currently working on integrating Lucia and Oslo with a Hono API and noticed the typing for the SameSite in the Cookie is not compatible with one used by Hono, when looking at the documentation on MDN website it seems that Hono are using the same one as MDN documentation.
Example
This gives a type error :
const session = await lucia.createSession(createdUser.userId, {});
const sessionCookie = lucia.createSessionCookie(session.id);
setCookie(ctx, sessionCookie.name, sessionCookie.value, {
...sessionCookie.attributes,
});
Argument of type '{ secure?: boolean; path?: string; domain?: string; sameSite?: "lax" | "strict" | "none"; httpOnly?: boolean; maxAge?: number; expires?: Date; }' is not assignable to parameter of type 'CookieOptions'.
Types of property 'sameSite' are incompatible.
Type '"lax" | "strict" | "none"' is not assignable to type '"Lax" | "Strict" | "None"'.
Type '"lax"' is not assignable to type '"Lax" | "Strict" | "None"'. Did you mean '"Lax"'?
And forced to do this instead:
const session = await lucia.createSession(createdUser.userId, {});
const sessionCookie = lucia.createSessionCookie(session.id);
setCookie(ctx, sessionCookie.name, sessionCookie.value, {
...sessionCookie.attributes,
sameSite: "Lax",
});
Suggested change
Change the typing to be in uppercase :
sameSite?: "None" | "Lax" | "Strict"
References :
If it's alright with you, I can create a PR to fix this issue
alphabet
is no longer exported in oslo/crypto
as previously in oslo/random
.
Hi! I didn't see the paragraph of contributing about the issue creation before open a PR (#29) . I think this is not a big change, since is an optional option and the default the method is still "S256". LMK if this can be merged or not. 👋
can you export the constant time equal function that you use in scrypt password util? :)
I'm currently working on integrating Lucia and Oslo with a Hono API and noticed the typing for the SameSite in the Cookie is not compatible with one used by Hono, when looking at the documentation on MDN website it seems that Hono are using the same one as MDN documentation.
Example
This gives a type error :
const session = await lucia.createSession(createdUser.userId, {});
const sessionCookie = lucia.createSessionCookie(session.id);
setCookie(ctx, sessionCookie.name, sessionCookie.value, {
...sessionCookie.attributes,
});
Argument of type '{ secure?: boolean; path?: string; domain?: string; sameSite?: "lax" | "strict" | "none"; httpOnly?: boolean; maxAge?: number; expires?: Date; }' is not assignable to parameter of type 'CookieOptions'.
Types of property 'sameSite' are incompatible.
Type '"lax" | "strict" | "none"' is not assignable to type '"Lax" | "Strict" | "None"'.
Type '"lax"' is not assignable to type '"Lax" | "Strict" | "None"'. Did you mean '"Lax"'?
And forced to do this instead:
const session = await lucia.createSession(createdUser.userId, {});
const sessionCookie = lucia.createSessionCookie(session.id);
setCookie(ctx, sessionCookie.name, sessionCookie.value, {
...sessionCookie.attributes,
sameSite: "Lax",
});
Suggested change
Change the typing to be in uppercase :
sameSite?: "None" | "Lax" | "Strict"
References :
If it's alright with you, I can create a PR to fix this issue
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.