OAuth2
An extensible, strongly-typed implementation of OAuth2 (RFC 6749).
Documentation is available on docs.rs. Release notes are available on GitHub.
Currently this library uses curl to perform HTTP requests. It'd be nice if we could add an async API that supports the tokio event loop. I'm not sure if it's possible with the curl bindings but it will allow us to use the async/await syntax in the future.
This is the tracking Issue for Release 4
rust-crypto
default-features = false
in openidconnet-rsIn 8.2 the spec allows for adding arbitrary, vendor-specific (or as described in 11.2, getting them registered) parameters for both requests and responses. As an example, in my case I'm working with OIDC, which is built on top of OAuth2, which makes use of this by using an extra id_token
field in the response for a JWT token. Would providing support for extra fields (not OIDC support itself) be in the scope of this library?
In Go everything unknown is stored into an untyped field that the user can retrieve.
I made a quick and dirty implementation of the above approach in here which works for me, but this doesn't yet support truly arbitrary fields, only string fields. If there's interest in it here I can flesh this out.
reqwest 0.10 just switched to http 0.2 (unreleased as of yet, reqwest 0.10.0-alpha.2 still uses http 0.1).
I saw that a lot of feature refactoring just happened in this project to support all combinations of futures 0.1 / 0.3 and reqwest 0.9 / 0.10. I don't know what's the best option to handle the version of the http
crate though. I see several options:
http
could either be bound to the selected reqwest version (0.2 for reqwest 0.10 and 0.1 for reqwest 0.9), orhttp_0_1
and http_0_2
, orI feel that the last option might be the best. It would require some http 0.1
<-> http 0.2
type translation code in case reqwest 0.9 is used.
Anyway, I just wanted to open this issue to point out this upcoming incompatibility.
I encountered this when trying to implement an authentication flow against Twitch.
You can see that their documentation specifies the correct scope format (i.e. space-separated string), but in the example it responds with an array of strings. I've verified that this is also the case with the actual API.
I don't know if I'm doing something wrong, nor how proliferate these kind of implementations are. But from a pragmatic perspective it would be nice to be able to use oauth2-rs
to get tokens from Twitch.
Since async/await is about to be stabilized (7. Nov as far as I know) I think it would be great to have a branch until then that prepares the crate for that.
extern crates...
Not sure why the nightly CI is failing, it works locally using nightly-2019-10-20
When trying to build an actix-web based client for the http requiest, I get errors about missing Send
markers. E.g. a stripped down example might be
async fn request()
{
let mut response = awc::ClientBuilder::new()
.finish()
.get("")
.send_body("")
.await
.unwrap();
response.body().await.unwrap();
}
async fn request_async<FN, F>(f: FN) -> ()
where
FN: FnOnce() -> F + Send,
F: std::future::Future<Output = ()> + Send, /// <<<< removing this Send makes it work
{
f().await
}
async fn auth()
{
request_async(request).await;
}
fn main() {
}
which results into
error[E0277]: `std::rc::Rc<awc::ClientConfig>` cannot be sent between threads safely
= help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<awc::ClientConfig>`
(and much more).
The request_auth()
function corresponds to oauth2:'s methods which have a similar signature.
When removing the Send
requirement from F
, things seem to be fine. In oauth2 this seems to be required due to #[async_trait]
but accordingly its documentation this can be explicitly disabled by writing
#[async_trait(?Send)]
Is there a reason why Send
is required for the http_client
or would it be possible to avoid it (which makes writing awc clients much easier)?
rust-1.40.0 awc-1.0.1 actix-web-2.0 (futures-0.3) oauth2-3.0.0-alpha.7
hey Alex
here are a few things I'd like improve:
[x] https://github.com/alexcrichton/oauth2-rs/pull/11 add tests
[x] https://github.com/alexcrichton/oauth2-rs/pull/13 have all the exchange_
methods return Result<Token, TokenError>
, change the TokenError#error
field to an enum (it's a strict set according to the RFC), but add also an Other(String)
value to the mix, where I'd map non-traditional errors and (json/form) parse errors
[x] https://github.com/alexcrichton/oauth2-rs/pull/12 change the interface of Config
a bit by adding builder-style field setters set_redirect_url(...) -> Config
, set_response_type(...) -> Config
, set_scopes(...) -> Config
(or maybe add_scope(...) -> Config
and work with one value at a time) so that setting up the whole OAuth2 flow would be as easy as:
let mut config = Config.new("id", "secret", "http://auth", "http://token").set_redirect_url("http://redirect").set_response_type("authorization_code").set_scopes(...);
let auth_url = config.authorize_url(...);
[ ] ~~determine whether to parse token data or error data based on the HTTP status code (but I'd have to see if there's anything about this in the specs or if different implementations handle this properly)~~
[ ] ~~the RFC says that scopes in the token response are separated by whitespace (not comma), so maybe implement something that handles both. I actually don't think this is so crucial but in some situations the user might authorize less scopes than the app actually requested. Google, for example, doesn't really allow that - you're either in or out.~~
[x] https://github.com/alexcrichton/oauth2-rs/pull/14 https://github.com/alexcrichton/oauth2-rs/pull/15 add some real-life examples: let's say Google and Github
[x] https://github.com/alexcrichton/oauth2-rs/pull/16 add some (basic) docs
it depends on how much free time I have so no promises, but in any case I'd like to hear your opinion first :)
I'm just starting to use this library to authenticate users using the "usual suspects" of providers (google, facebook, github etc...). To do so, I'm thinking I will need to initialize a collection of BasicClient
types (a Client
). The specifics for each would be hosted in a configuration file. An efficient way to do so would be to Deserialize
the BasicClient
values from the configuration.
What is the reason for not implementing the Serde traits for something like BasicClient
?
Implemented #97
- adding a Client::set_introspection_url method for setting the URL (which can be leveraged by multiple subsequent introspection calls, similar to the pattern for setting the auth_url xand token_url in Client::new, but without introducing a breaking change to that method) :heavy_check_mark:
- adding a Client::introspect_token method that follows the same pattern as exchange_code, etc. (i.e., it should return an IntrospectionRequest struct). :heavy_check_mark:
- defining a strongly-typed IntrospectionResponse struct leveraging the NewType pattern where appropriate :heavy_check_mark:
- adding appropriate unit tests :heavy_check_mark:
Better to add features to turn on rustls in reqwest
.
Library native-tls
is dynamically linked and it's hard sometimes to set up the environment for it. I added features that enable rustls in reqwest but by default reqwest
uses native-tls
in feature default-tls
and needs to be disabled to make work rustls.
So to use rustls with oauth2 all you need to specify:
oauth2 = { version = "3.0", features = ["reqwest-010", "reqwest-010-rustls"], default-features = false }
Hello
I got this parse error response when using let token_to_revoke = match Some(refresh_token) { Some(token) => token.into(), None => (&access_token).into(), }; client = client.set_revocation_uri( RevocationUrl::new(self.oauth2_endpoint.revocation_endpoint.clone()).unwrap(), ) client .revoke_token(token_to_revoke) .expect("This must have the right token") .request_async ...
I am not sure if I have some lacking steps in setting up sa client. Please let me know what I am missing.
Request: Request: URL=Url { scheme: "https", cannot_be_a_base: false, username: "", password: None, host: Some(Domain("graph.microsoft.com")), port: None, path: "/beta/me/invalidateAllRefreshTokens", query: None, fragment: None } Request: Header={"accept": "application/json", "content-type": "application/x-www-form-urlencoded"} Request: Method=POST Request: Body="token=M.R3_BL2.-CdSr1AhpVWufEK2FqWxHUAYved1A1yZu7o74VLPI9SxUVuf1pzn0roqgCOP6GLJaSULYpsVitIIwx6QeBxtOLOados1q7LxLrvdx6Z%21DnUyfrqeBSGdGvc4E4c6YfHOw8hVQHJYwpG240j74YLoi%21srUJWIqxDFwbVkCYC43rkFu4V2BwWg3MFdr7dGQYEaoNejGMIxsEQ2Nk%21T65wnxpoHfKyUNNqx4oTNssUhosJKn5F9dhQ5RkG2fHyezOZGycPfSnIO2zJwNAISFRQNgNlqQIspF2Z4ytnS6urTfy7I9yvRnGtSJPw%24%24&token_type_hint=refresh_token&client_id=CLIENT_ID"
Response: {"error":{"code":"InvalidAuthenticationToken","message":"Access token is empty.","innerError":{"date":"2022-11-24T07:25:58","request-id":"51403c6c-617a-4277-b1e1-06ba56269218","client-request-id":"51403c6c-617a-4277-b1e1-06ba56269218"}}}
It seems like secret types like PkceCodeVerifier are simply wrapping a string; is there a reason they don't implement Clone
?
If I need to clone my verifier, is it fine to simply grab the secret from the existing one and use it to create a new one? Like so:
fn clone_verifier(verifier: &PkceCodeVerifier) -> PkceCodeVerifier {
PkceCodeVerifier::new(verifier.secret().clone())
}
Hello
I found a problem with DeviceAccessTokenRequest when using it as async. There is an async implementation found on your codes but there is a compile error when using it. I found a sample but it is a sync example https://docs.rs/oauth2/latest/oauth2/#device-code-flow. I am creating an async implementation of it.
pub async fn get_token(&mut self) -> Result<UserToken, Box<dyn std::error::Error>>
{
let request_token =
self.client.as_ref().unwrap()
.exchange_device_access_token(&self.devicecode_result.as_ref().unwrap());
let token_result = request_token.request_async(async_http_client, std::thread::sleep, None).await?;
println!("Access Token: {}", token_result.clone().access_token().secret().to_string());
Ok(UserToken{token: token_result.clone().access_token().secret().to_string()})
}
Compile Errors:
.request_async(async_http_client, std::thread::sleep, None).await?;
| ^^^^^^^^^^^^^ ()
is not a future
2363 | SF: Future<Output = ()>,
| ^^^^^^^^^^^^^^^^^^^ required by this bound in DeviceAccessTokenRequest::<'a, 'b, TR, TT, EF>::request_async
We are building an SPA in the frontend. Our design at this point involves navigating to the third party, and in the redirect, setting things up to then pass to the backend to obtain the access token.
The api of this library has raised some questions:
code
is to use some form of persistence as the frontend sees it (e.g. through local storage)BasicClient
requires reconstruction in the backendAlternatively, we could rejig our design such that it is all handled in the backend (i.e. the backend sends the authorisation url to the front end, but redirects to speak with the backend), and that might be the way we will end up going, but would rather do that because it's a choice in design, rather than a limitation of the api.
For context, the reason why we are doing the flow in the backend, rather than the frontend, is because when trying to authorize with github, it's blocked by CORS.
Fixes #179
This PR attempts to reduce unnecessary lifetimes. This is unfortunately at the expense of a little bit more complexity in handling the Futures. This effect could maybe be reduced somewhat with the use of the futures
crate.
The other significant change here is the change from Arc<Fn() -> DateTime + 'b>
to a type parameter which reduced the complexity of the future and I believe reduced the complexity of the lifetimes of DeviceAcessTokenRequest
. However this is a breaking change!
When using request_async
, the async
function implicity captures self
and therefore can lead to restricted lifetimes. For example
Given the following wrapper around BasicClient
pub struct Oauth2Client(oauth2::basic::BasicClient);
impl Oauth2Client {
pub fn request_token(
&self,
code: AuthorizationCode,
verifier: PkceCodeVerifier,
) -> impl Future<
Output = Result<
BasicTokenResponse,
RequestTokenError<AsyncHttpClientError, BasicErrorResponse>,
>,
> {
self.0
.exchange_code(code)
.set_pkce_verifier(verifier)
.request_async(oauth2::reqwest::async_http_client)
}
}
This won't compile with the following error:
error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds
--> src/main.rs:17:9
|
8 | &self,
| ----- hidden type `impl Future<Output = Result<StandardTokenResponse<EmptyExtraTokenFields, BasicTokenType>, oauth2::RequestTokenError<oauth2::reqwest::Error<reqwest::error::Error>, StandardErrorResponse<BasicErrorResponseType>>>>` captures the anonymous lifetime defined here
...
17 | / self.0
18 | | .exchange_code(code)
19 | | .set_pkce_verifier(verifier)
20 | | .request_async(oauth2::reqwest::async_http_client)
| |______________________________________________________________^
|
help: to declare that the `impl Trait` captures `'_`, you can add an explicit `'_` lifetime bound
|
16 | > + '_ {
This is because CodeTokenRequest
has a lifetime associated with Client
and then subsequently gets captured in the future generated by request_async
. We can modify request_async
to return a non-capturing future instead.
pub fn request_async<C, F, RE>(
self,
http_client: C,
) -> impl Future<Output = Result<TR, RequestTokenError<RE, TE>>>
where
C: FnOnce(HttpRequest) -> F,
F: Future<Output = Result<HttpResponse, RE>>,
RE: Error + 'static,
{
futures::future::ready(self.prepare_request()).and_then(|http_request| {
http_client(http_request).map(|res| {
res.map_err(RequestTokenError::Request)
.and_then(endpoint_response)
})
})
}
which will then not have the implicit lifetime and allow for the previous code to compile. I'm happy to implement this. I've used futures
here which isn't a dependency, but I could create a custom Future as all request_async
functions are basically the same with the exception DeviceAccessTokenRequest::request_async
.
const
functions (#186)ureq::http_client
use send_bytes()
for POST requests to avoid sending Transfer-Encoding: chunked
header that certain providers such as Microsoft do not support (#182/#183)RequestTokenError::ServerResponse
with DeviceCodeErrorResponseType::ExpiredToken
(instead of RequestTokenError::Other
) when device access token request times out (#195)Client
struct (#167)sha2
and hmac
dependencies (#174)set_redirect_uri
docs typo (#168)reqwest
version mentioned in docs (#154)add_scopes
convenience methods to request objects (#138)set_redirect_uri
method to CodeTokenRequest
(#144)DeviceAccessTokenRequest::request_async
(#152)failed
instead of Errored
.reqwest
to 0.11 and rename feature flag to reqwest
. This upgrades tokio
to 1.0 and removes support for both the reqwest-010
and reqwest-09
feature flags.futures
0.1 and remove the futures-01
and futures-03
feature flags; only async/await and futures
0.3 are now supported (without requiring any feature flags).Async*
traits and move the request_async
methods to the underlying *Request
structshttp
0.1 to 0.2.std::error::Error
instead of failure::Fail
.serde_path_to_error::Error<serde_json::Error>>
in the RequestTokenError::Parse
variant instead of a serde_json::Error
to make JSON deserialization errors easier to diagnose.#[non_exhaustive]
attribute to AuthType
to support non-breaking additions in the future.plain
(plaintext) PKCE verifier when the (non-default) pkce-plain
feature flag is enabled. Use of this feature is discouraged for security reasons.ureq
HTTP client (#119).reqwest
client use rustls-tls
by default instead of native TLS. This behavior can be overridden using the native-tls
feature flag.rust-crypto
with hmac
in dev-dependenciesThis is the first beta release for the 4.0 major version. No further breaking changes are expected until the next major version.
rustls-tls
(default) and native-tls
feature flags for use with reqwest
. Previously, enabling the reqwest
feature flag would always use rustls
. The default behavior is unchanged, but users that disable the default features and wish to continue using rustls
may wish to add the rustls-tls
feature flag to their Cargo.toml
.serde_path_to_error::Error<serde_json::Error>>
in the RequestTokenError::Parse
variant instead of a serde_json::Error
. This change should make JSON deserialization errors easier to diagnose.set_introspection_url
-> set_introspection_uri
set_redirect_url
-> set_redirect_uri
set_revocation_url
-> set_revocation_uri
Client::exchange_device_code
, Client::introspect
, and Client::revoke_token
fail fast with a new ConfigurationError
enum when the relevant OAuth2 endpoint hasn't been configured by calling set_device_authorization_url
, set_introspection_url
, or set_revocation_url
, respectively. Previously, an error would not be returned until a call to request
/request_async
(#127).extra_fields()
getter to StandardTokenIntrospectionResponse (#126)IntrospectRequest
to IntrospectionRequest
*TokenInspectionResponse
to *TokenIntrospectionResponse
IntrospectUrl
to IntrospectionUrl
introspect_url
to introspection_url
.Add support for OAuth 2.0 Token Revocation (RFC 7009) (#122).
Special thanks to @ximon18 for contributing this feature.
Add ureq http_client (#119)
reqwest
to 0.11 and rename feature flag from reqwest-010
to reqwest
. This upgrades tokio
to 1.0.#[non_exhaustive]
attribute to AuthType
to support non-breaking additions in the futureAdd support for the plain
(plaintext) PKCE verifier when the (non-default) pkce-plain
feature flag is enabled. Use of this feature is discouraged for security reasons.
Add support for OAuth 2.0 Token Introspection.
Special thanks to @jeroenvervaeke for contributing this feature.
Add support for OAuth 2.0 Device Authorization Grant.
Special thanks to @maxdymond for contributing this feature.
reqwest
0.9 (previously enabled via the reqwest-09
feature flag); only the (default) reqwest-010
feature flag is now supportedhttp
0.1 to 0.2futures
0.1 and remove the futures-01
and futures-03
feature flags; only async/await is now supported (without requiring any feature flags)Async*
traits and move the request_async
methods to the underlying *Request
structsstd::error::Error
instead of failure::Fail
Send
and Sync
trait bounds on error types to improve compatibility with actix
reqwest
client use rustls-tls
instead of the default native TLSserde
feature flag on url
crate dependencysha2
dependencyrust-crypto
with hmac
in dev-dependenciesunicode-normalization
dependencymaster
branch to main
This version is identical to 3.0.0-alpha.10 but is no longer considered to be a pre-release.
Source code(tar.gz)AuthorizationRequest::set_redirect_url
methodsha2
, rand
, and base64
dependencieshex
0.4.0 to maintain Rust 1.34 compatibilityExtension
variants to BasicTokenType
and BasicErrorResponseType
enumsreqwest-010
feature flag to use reqwest
0.10http
and url
cratesThis release is identical to 3.0.0-alpha.7
except that it pins specifically
to reqwest
version 0.10.0-alpha.2
when the request-010
feature
is enabled. This prevents Cargo's SemVer functionality from automatically
using reqwest
0.10, with which this crate is not yet compatible.
reqwest
, renamed the reqwest
feature flag to reqwest-09
and added a separate reqwest-010
flag separate from futures-03
. The default feature flag remains reqwest-09
. Added new docs describing the various HTTP client interfaces.async_internal
module from public API (previously included accidentally).async-std
dependencyactix-web-oauth2
example in docsfrom_url
constructor to URL-based NewTypes (AuthUrl, TokenUrl, RedirectUrl)reqwest::{HttpClientError, FutureHttpClientError, AsyncHttpClientError}
With the default feature flags, this crate no longer includes async support. To enable legacy async support using futures 0.1, use the futures-01
feature flag in Cargo.toml
. To enable async/await support (requires rustc
1.39.0 or newer), use the futures-03
feature.
Special thanks to @marcelbuesing for contributing this support.
The URL-based NewTypes (AuthUrl
, TokenUrl
, RedirectUrl
) no longer accept a Url
type in their constructors. Instead, they parse the URLs internally, as well as maintain an unparsed copy of the input string, which is returned by Deref
. This avoids the URL canonicalization performed by the url
crate (e.g., adding a trailing slash for URLs with no path component). Previously, this canonicalization sometimes caused mismatches when comparing redirect URIs, which are expected to be identical. A parsed version of the URL can still be accessed by using the new .url()
method.
url
crate from 1.0 to 2.1Clone
for CsrfToken
Promote 2.0.0-beta.3 to 2.0.0.
Source code(tar.gz)PartialEq
derivations on secret types (unsafe, non-constant-time comparisons vulnerable to timing attacks)Clone
for StandardTokenReponse
request_async
API for non-code flowsEq
and Hash
for String
new typesStandardTokenResponse::set_expires_in
take Duration
for consistencyThe 3.0.0-alpha.1 release included a new HTTP client that utilizes the reqwest crate. By default, reqwest follows HTTP redirects. This allows a malicious OAuth2 authorization server to redirect token endpoint requests to arbitrary URLs, including internal addresses reachable from the client. Such a redirect can be used to mount an SSRF attack.
Versions prior to 3.0.0-alpha.1 are not affected. Users of 3.0.0-alpha.1 are encouraged to upgrade to 3.0.0-alpha.2 or a newer release and are discouraged from using any alpha release in a production environment.
Thanks to @d0nutptr for helping to discover this issue!
Source code(tar.gz)Experimental new API with support for asynchronous requests and arbitrary HTTP clients.
This release features significant breaking changes from the 2.x release branch:
curl
and reqwest
are included as HTTP clients; users may implement their own (passed to request
or request_async
for each token exchange).insecure
module has been removed.rand
pin to 0.6TokenResponse
and ErrorResponse
.The 2.x API is now stable! No further breaking changes are expected until a future 3.x release.
This release is a promotion of 2.0.0-alpha.5 to being API stable. It contains no additional changes.
Source code(tar.gz)oxide-auth A OAuth2 server library, for use in combination with common web servers, featuring a set of configurable and pluggable backends. About oxid
envlt envlt, like env, allows you to define environment variables and then execute into something else, but instead of static values, it uses using si
Rust : Forbidden (WIP) An experimental auth library for Rust applications. Goals This crate is to define a common set of traits and idioms to provide
Rust library for HTTP authentication. Parses challenge lists, responds to Basic and Digest challenges. Likely to be extended with server support and a
JWT Vault Highly flexible library to manage and orchestrate JWT workflow Examples | Website | Chat TODO Add more examples Improve coverage Features Ma
jsonwebtoken API documentation on docs.rs See JSON Web Tokens for more information on what JSON Web Tokens are. Installation Add the following to Carg
This project contains a Rust server that serves a single page application and has authentication + JWT-based authorization.
authorization-server Authorization Server with Rust using Tonic. Function implemented User registration and profile store Change password Login Token
Paseto Rust Paseto is everything you love about JOSE (JWT, JWE, JWS) without any of the many design deficits that plague the JOSE standards. This is d
OpenSK This repository contains a Rust implementation of a FIDO2 authenticator. We developed this as a Tock OS application and it has been successfull
Authenticating to Minecraft with the Microsoft Authentication Scheme from Rust This program showcases an implementation of the microsoft authenticatio
ROCCA for Rust This is a Rust implementation of the ROCCA authenticated cipher, ported from the Zig implementation. ROCCA is key committing, has a 256
stormi Stormi is a fast and simple file-server with public/private key authentication How does it work? Stormi accepts multipart/form-data form with m
RSA A portable RSA implementation in pure Rust. ⚠️ WARNING: This crate has been audited by a 3rd party, but a full blog post with the results and the
Fire Auth Rust wrapper for Firebase Authentication REST API Installation Add the following to Cargo.toml: fireauth = "0.1.5" How to use First you need
Rusty JWT Tools A collection of JWT utilities. This repository is part of the source code of Wire. You can find more information at wire.com or by con
MC Auth Xbox live authentication flow for Minecraft in Rust. Why? In order to create tools for Minecraft based on rust that implement the user profile
apple-private-apis A set of Rust libraries to interact with apple's private APIs and servers, made for use in SideInstaller. Library Description omnis
WASM OIDC Plugin A plugin for Envoy written in Rust. It is a HTTP Filter, that implements the OIDC Authorization Code Flow. Requests sent to the filte