udibo / oauth2_server Goto Github PK
View Code? Open in Web Editor NEWA standards compliant implementation of an OAuth 2.0 authorization server with PKCE support.
License: MIT License
A standards compliant implementation of an OAuth 2.0 authorization server with PKCE support.
License: MIT License
Currently there is no locking in getAccessToken around the refresh token code. If 2 requests come in at the same time, this could result in the token api being called twice for the same refresh token. That would cause the token to get revoked due to token replay prevention measures.
I added getAccessToken as an argument to authenticate to make it easy to have alternative methods of getting an accessToken. This makes it possible to get an access token from a session without having to modify the request to have an authentication header that includes the session's accessToken.
The problem with getAccessToken is that it doesn't really give a way to work around token expiration. If I change the function to getToken, I could have it first call server.getToken with the session's accessToken. Then if the token is invalid, I could have it make an api call to refresh the token for the session before calling getToken again with the newly refreshed accessToken.
The example will need some sort of locking around refreshing tokens so that the concurrent requests don't try to refresh already refreshed tokens. If the refresh token is invalid or expired, it will need to delete the session and return an AccessDenied error.
If you look at the examples/oak-localstorage, the AuthorizationCode and Token models had to be extended just to set the Client and User types. If the models and services that use them used generics, those models would not need to be implemented by end users.
std/hash has been deprecated now that the Web Crypto API is able to handle hashing.
This change will reduce the size of resource_server.ts from 308.8 kB to 116.4 kB and authorization_server.ts from 345.1 kB to 152.7 kB.
Add an allowRefreshToken function to token service for determining if a refresh token should be generated based on client, user, and scope. This could be useful for granting temporary authorization that will end when the access token expires. Keep allowRefreshToken option on the grants so that it can still be easily disabled for all token requests to a grant type. If allowed for the grant, then call allowRefreshToken for the service. Default new function to resolving to true since it already requires refresh tokens be enabled at the grant level.
https://deno.land/x/[email protected]
For this, I'll need to update the code that generates the error code to base it on the name. Exclude error from all codes but server_error. Do not expose the server errors beyond the name and status of it.
Depends on #12
This module should have a built in jwt service for resource servers to use. jwts give resource servers the ability to authenticate a route without having to make a call to the authorization server. It should extend the AbstractAccessTokenService and generate the AccessToken object from the jwt access token.
Add example of resource server and authorization server that use jwt access tokens. The services description should include the inability to revoke jwt access tokens as a disadvantage of using jwts and how short access token lifetime can help reduce the security risk.
https://datatracker.ietf.org/doc/html/rfc7009#page-5
It would be good to have a standardized way of revoking tokens instead of having to come up with custom revoke methods. In the oak localstorage example I made, currently the only way to revoke tokens is if you have a session, you can go to the logout url. The logout url will revoke both the access and refresh tokens.
An introspection function should be added to the OAuth2Server and Oak adapter in a similar way to token, authenticate, and authorize.
See RFC 7662 for more information about token introspection.
Currently the example manually refreshes token when it is expired. Create an OAuth2Client object that can handle refreshing tokens and handling authorization flow. The client should have a fetch function to make it easier to make fetch requests to API endpoints that use OAuth2 for authentication.
Add getClientForRequest to server and oak adapter. Add getClientForContext to oak adapter. This will behave similar to getTokenForRequest except it will instead return an OAuth2Client that is ready to make requests for the user. For this to work, the server would need to take some configuration options like the authorizeUrl and tokenUrl.
I added documentation as I was writing oauth2_server but it could use better documentation with actual examples in the documentation. Currently I only have a usage example that doesn't cover everything and it is not included in the deno docs.
@KyleJune this is great. Would be amazing if you could create a production example of how this and oak work together.
I think it'll generate a lot of attention.
Not many modules focus on production applications for Deno... that could become a case study. Happy to help you write a blog post about it if you like.
Update hashPassword function on the user service to use PBKDF2 for password hashing.
https://github.com/udibo/oauth2_server/blob/main/services/user.ts
I wrote a function for generating salt in the example. It should be moved to the user service so that it's more reusable like the hashPassword function is.
https://github.com/udibo/oauth2_server/blob/main/examples/oak-localstorage/services/user.ts#L4
To be determined, what options should hashPassword have configurable.
Notes:
https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/deriveBits#pbkdf2
import { encode as encodeHex } from "https://deno.land/[email protected]/encoding/hex.ts";
function generateSalt(): Uint8Array {
return window.crypto.getRandomValues(new Uint8Array(16));
}
/*
Get some key material to use as input to the deriveBits method.
The key material is a password supplied by the user.
*/
function getKeyMaterial(password: string) {
const enc = new TextEncoder();
return window.crypto.subtle.importKey(
"raw",
enc.encode(password),
"PBKDF2",
false,
["deriveBits", "deriveKey"],
);
}
/*
Derive some bits from a password supplied by the user.
*/
async function getDerivedBits(
password: string,
salt: Uint8Array,
): Promise<[Uint8Array, number]> {
const keyMaterial = await getKeyMaterial(password);
const derivedBits = await window.crypto.subtle.deriveBits(
{
"name": "PBKDF2",
salt,
"iterations": 100000,
"hash": "SHA-256",
},
keyMaterial,
256,
);
const buffer = new Uint8Array(derivedBits, 0, 32);
return [buffer, derivedBits.byteLength];
}
const salt = generateSalt();
const saltText = (new TextDecoder()).decode(encodeHex(salt));
console.log("salt: ", saltText);
const [buffer, length] = await getDerivedBits("hunter2", salt);
console.log(buffer);
console.log(length);
const hashed = (new TextDecoder()).decode(encodeHex(new Uint8Array(buffer)));
console.log(hashed);
// current SHA-256 hashPassword function
async function hashPassword(password: string, salt?: string): Promise<string> {
const data = (new TextEncoder()).encode(
password + (salt ? `:${salt}` : ""),
);
const buffer = await crypto.subtle.digest("SHA-256", data);
return (new TextDecoder()).decode(encodeHex(new Uint8Array(buffer)));
}
console.log(await hashPassword("hunter2", saltText));
Currently the oak server uses the default error handler for errors that are not handled. Create a simple error handler that logs the error and returns an html page showing the error.
The oak-localstorage example uses a session instead of sending the access token and refresh token to the client. I did this because I wasn't sure about best way to store refresh token in the client.
I now believe it is safe to store the refresh token and access token in http only secure cookies. To avoid sending the refresh token with every request, the refresh token cookie can have the token endpoint specified as the cookie path.
https://stackoverflow.com/questions/68620374/store-access-token-and-refresh-token-in-cookies
To make this change, I will need to update the refresh token grant to have a callback option for an alternative way of getting the refresh token. In the example I will have it check for a refresh token cookie.
The getAccessToken option should be moved to the ResourceServer constructor since there would only ever be one getAccessToken function for a resource server. The Oak adapter for resource server currently takes getAccessToken in the constructor. Once this change is made, that can be removed. The getAccessToken function will no longer need a requireRefresh argument since refreshing will be left to the client.
I have another issue open that will make it easier for the client to handle request failures due to access token expiration and refreshing tokens.
If the custom access token value is invalid, authenticate should call getAccessToken again with a flag indicating that the access token needs refreshed. If it cannot be refreshed, that function should return null to indicate that authentication is required.
This function was a recent addition and needs test coverage.
If the function returns false, that means it rejected the requested scope.
If the function returns undefined, that means it accepted the request but the request either did not request scope or the request was accepted but the token was not granted any scopes. The scoped returned does not need to match the scope passed in.
Test coverage should be added to grants/grant_test.ts, grants/client_credentials_test.ts, grants/password_test.ts, and server_test.ts.
The example currently does not have any test coverage. I have only manually tested the example.
Currently the service's patch functions use setting properties to undefined to clear out fields on an entity. Change to setting to null to remove the field from the entity so that the function doesn't depend on using the in
operator for determining if field should be removed.
I think it would be good to include an implementation of the device authorization grant in the grants folder so that people that would like to use this grant can easily add it to their OAuth2 Server.
See [RFC 8628] for more information about the device authorization grant.
OAuth2Server currently has everything on it. The resource server wouldn't need everything from it since it just needs to authenticate routes. Update readme to include that the main entrypoints are resource_server.ts and authorization_server.ts. delete mod.ts and add doc links for each sub-module.
To be able to use oauth2 for views,this cannot use authenticate because it responds to the request with a json error if authenticate fails and it doesn't request authentication when user is not logged in. The resource server need a separate function for authenticating the view so that it can leave the handling of errors to the routing framework and request authentication when user is not logged in.
I'm currently working on building a Software as a Service that will make use of this library. I've paused development on this while I continue working on the other parts that will make use of this library. I have some issues open for changes I plan on making in the future to this.
Once my Software as a Service is up and running, I will create examples for how to use this with different frameworks like Oak and Fresh.
Due to this being a side project that I don't work on full time, expect progress to be slow. I have a full time job so I really only have time to work on my side project some nights and on weekends. I might not continue development on this specific module until later in the year or early next year.
This appears to be a bug in deno. I attempted to resolve the bug by changing how I export/import Scope but was unsuccessful. Watch the above issue on deno for either the issue getting fixed in deno or a possible way to resolve the issue in this module.
Until this issue is resolved, this module cannot be used with the --no-check flag.
SQL databases in particular usually use number
IDs for their rows/documents.
This feature would mean the developer has to write less boilerplate number to string conversion code to make everything work.
The oak adapter is missing test coverage for the token, authorize, and authenticate functions.
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.