mirror of
https://github.com/OMGeeky/yup-oauth2.git
synced 2026-01-19 16:54:47 +01:00
@@ -74,7 +74,7 @@ pub enum RequestError {
|
||||
HttpError(hyper::Error),
|
||||
/// The OAuth client was not found
|
||||
InvalidClient,
|
||||
/// Some requested scopes were invalid. String contains the scopes as part of
|
||||
/// Some requested scopes were invalid. String contains the scopes as part of
|
||||
/// the server error message
|
||||
InvalidScope(String),
|
||||
/// A 'catch-all' variant containing the server error and description
|
||||
@@ -99,7 +99,7 @@ impl fmt::Display for RequestError {
|
||||
match *self {
|
||||
RequestError::HttpError(ref err) => err.fmt(f),
|
||||
RequestError::InvalidClient => "Invalid Client".fmt(f),
|
||||
RequestError::InvalidScope(ref scope)
|
||||
RequestError::InvalidScope(ref scope)
|
||||
=> writeln!(f, "Invalid Scope: '{}'", scope),
|
||||
RequestError::NegativeServerResponse(ref error, ref desc) => {
|
||||
try!(error.fmt(f));
|
||||
@@ -135,7 +135,7 @@ impl fmt::Display for PollError {
|
||||
}
|
||||
|
||||
|
||||
impl<C> DeviceFlow<C>
|
||||
impl<C> DeviceFlow<C>
|
||||
where C: BorrowMut<hyper::Client> {
|
||||
|
||||
/// # Examples
|
||||
@@ -143,7 +143,7 @@ impl<C> DeviceFlow<C>
|
||||
/// extern crate hyper;
|
||||
/// extern crate yup_oauth2 as oauth2;
|
||||
/// use oauth2::DeviceFlow;
|
||||
///
|
||||
///
|
||||
/// # #[test] fn new() {
|
||||
/// let mut f = DeviceFlow::new(hyper::Client::new());
|
||||
/// # }
|
||||
@@ -161,14 +161,14 @@ impl<C> DeviceFlow<C>
|
||||
|
||||
/// The first step involves asking the server for a code that the user
|
||||
/// can type into a field at a specified URL. It is called only once, assuming
|
||||
/// there was no connection error. Otherwise, it may be called again until
|
||||
/// there was no connection error. Otherwise, it may be called again until
|
||||
/// you receive an `Ok` result.
|
||||
/// # Arguments
|
||||
/// * `client_id` & `client_secret` - as obtained when [registering your application](https://developers.google.com/youtube/registering_an_application)
|
||||
/// * `scopes` - an iterator yielding String-like objects which are URLs defining what your
|
||||
/// * `scopes` - an iterator yielding String-like objects which are URLs defining what your
|
||||
/// application is able to do. It is considered good behaviour to authenticate
|
||||
/// only once, with all scopes you will ever require.
|
||||
/// However, you can also manage multiple tokens for different scopes, if your
|
||||
/// However, you can also manage multiple tokens for different scopes, if your
|
||||
/// application is providing distinct read-only and write modes.
|
||||
/// # Panics
|
||||
/// * If called after a successful result was returned at least once.
|
||||
@@ -176,7 +176,7 @@ impl<C> DeviceFlow<C>
|
||||
/// See test-cases in source code for a more complete example.
|
||||
pub fn request_code<'b, T, I>(&mut self, client_id: &str, client_secret: &str, scopes: I)
|
||||
-> Result<PollInformation, RequestError>
|
||||
where T: AsRef<str>,
|
||||
where T: AsRef<str> + 'b,
|
||||
I: IntoIterator<Item=&'b T> {
|
||||
if self.state.is_some() {
|
||||
panic!("Must not be called after we have obtained a token and have no error");
|
||||
@@ -246,16 +246,16 @@ impl<C> DeviceFlow<C>
|
||||
|
||||
/// If the first call is successful, this method may be called.
|
||||
/// As long as we are waiting for authentication, it will return `Ok(None)`.
|
||||
/// You should call it within the interval given the previously returned
|
||||
/// You should call it within the interval given the previously returned
|
||||
/// `PollInformation.interval` field.
|
||||
///
|
||||
/// The operation was successful once you receive an Ok(Some(Token)) for the first time.
|
||||
/// Subsequent calls will return the previous result, which may also be an error state.
|
||||
///
|
||||
/// Do not call after `PollError::Expired|PollError::AccessDenied` was among the
|
||||
/// Do not call after `PollError::Expired|PollError::AccessDenied` was among the
|
||||
/// `Err(PollError)` variants as the flow will not do anything anymore.
|
||||
/// Thus in any unsuccessful case which is not `PollError::HttpError`, you will have to start /// over the entire flow, which requires a new instance of this type.
|
||||
///
|
||||
///
|
||||
/// > ⚠️ **Warning**: We assume the caller doesn't call faster than `interval` and are not
|
||||
/// > protected against this kind of mis-use.
|
||||
///
|
||||
@@ -264,7 +264,7 @@ impl<C> DeviceFlow<C>
|
||||
pub fn poll_token(&mut self) -> Result<Option<Token>, &PollError> {
|
||||
// clone, as we may re-assign our state later
|
||||
let pi = match self.state {
|
||||
Some(ref s) =>
|
||||
Some(ref s) =>
|
||||
match *s {
|
||||
DeviceFlowState::Pending(ref pi) => pi.clone(),
|
||||
DeviceFlowState::Error => return Err(self.error.as_ref().unwrap()),
|
||||
@@ -286,7 +286,7 @@ impl<C> DeviceFlow<C>
|
||||
("code", &self.device_code),
|
||||
("grant_type", "http://oauth.net/grant_type/device/1.0")]);
|
||||
|
||||
let json_str =
|
||||
let json_str =
|
||||
match self.client.borrow_mut().post(GOOGLE_TOKEN_URL)
|
||||
.header(ContentType("application/x-www-form-urlencoded".parse().unwrap()))
|
||||
.body(&*req)
|
||||
@@ -403,7 +403,7 @@ pub mod tests {
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
let t =
|
||||
let t =
|
||||
match flow.poll_token() {
|
||||
Ok(Some(t)) => {
|
||||
assert_eq!(t.access_token, "1/fFAGRNJru1FTz70BzhT3Zg");
|
||||
|
||||
@@ -79,7 +79,7 @@ impl TokenStorage for MemoryStorage {
|
||||
}
|
||||
}
|
||||
|
||||
/// A generalized authenticator which will keep tokens valid and store them.
|
||||
/// A generalized authenticator which will keep tokens valid and store them.
|
||||
///
|
||||
/// It is the go-to helper to deal with any kind of supported authentication flow,
|
||||
/// which will be kept valid and usable.
|
||||
@@ -92,7 +92,7 @@ impl TokenStorage for MemoryStorage {
|
||||
/// * presenting the user code
|
||||
/// * inform the user about the progress or errors
|
||||
/// * abort the operation
|
||||
///
|
||||
///
|
||||
/// # Usage
|
||||
/// Please have a look at the library's landing page.
|
||||
pub struct Authenticator<D, S, C> {
|
||||
@@ -151,7 +151,7 @@ impl Error for StringError {
|
||||
/// if no user is involved.
|
||||
pub trait GetToken {
|
||||
fn token<'b, I, T>(&mut self, scopes: I) -> Result<Token, Box<Error>>
|
||||
where T: AsRef<str> + Ord,
|
||||
where T: AsRef<str> + Ord + 'b,
|
||||
I: IntoIterator<Item=&'b T>;
|
||||
|
||||
fn api_key(&mut self) -> Option<String>;
|
||||
@@ -162,21 +162,21 @@ impl<D, S, C> Authenticator<D, S, C>
|
||||
S: TokenStorage,
|
||||
C: BorrowMut<hyper::Client> {
|
||||
|
||||
|
||||
|
||||
/// Returns a new `Authenticator` instance
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `secret` - usually obtained from a client secret file produced by the
|
||||
/// * `secret` - usually obtained from a client secret file produced by the
|
||||
/// [developer console][dev-con]
|
||||
/// * `delegate` - Used to further refine the flow of the authentication.
|
||||
/// * `client` - used for all authentication https requests
|
||||
/// * `storage` - used to cache authorization tokens tokens permanently. However,
|
||||
/// the implementation doesn't have any particular semantic requirement, which
|
||||
/// is why `NullStorage` and `MemoryStorage` can be used as well.
|
||||
/// * `flow_type` - the kind of authentication to use to obtain a token for the
|
||||
/// * `flow_type` - the kind of authentication to use to obtain a token for the
|
||||
/// required scopes. If unset, it will be derived from the secret.
|
||||
/// [dev-con]: https://console.developers.google.com
|
||||
pub fn new(secret: &ApplicationSecret,
|
||||
pub fn new(secret: &ApplicationSecret,
|
||||
delegate: D, client: C, storage: S, flow_type: Option<FlowType>)
|
||||
-> Authenticator<D, S, C> {
|
||||
Authenticator {
|
||||
@@ -194,7 +194,7 @@ impl<D, S, C> Authenticator<D, S, C>
|
||||
// PHASE 1: REQUEST CODE
|
||||
let pi: PollInformation;
|
||||
loop {
|
||||
let res = flow.request_code(&self.secret.client_id,
|
||||
let res = flow.request_code(&self.secret.client_id,
|
||||
&self.secret.client_secret, scopes.iter());
|
||||
|
||||
pi = match res {
|
||||
@@ -232,7 +232,7 @@ impl<D, S, C> Authenticator<D, S, C>
|
||||
match poll_err {
|
||||
&&PollError::HttpError(ref err) => {
|
||||
match self.delegate.connection_error(err) {
|
||||
Retry::Abort|Retry::Skip
|
||||
Retry::Abort|Retry::Skip
|
||||
=> return Err(Box::new(StringError::from(err as &Error))),
|
||||
Retry::After(d) => sleep_ms(d.num_milliseconds() as u32),
|
||||
}
|
||||
@@ -247,9 +247,9 @@ impl<D, S, C> Authenticator<D, S, C>
|
||||
},
|
||||
}; // end match poll_err
|
||||
},
|
||||
Ok(None) =>
|
||||
Ok(None) =>
|
||||
match self.delegate.pending(&pi) {
|
||||
Retry::Abort|Retry::Skip
|
||||
Retry::Abort|Retry::Skip
|
||||
=> return Err(Box::new(StringError::new("Pending authentication aborted".to_string(), None))),
|
||||
Retry::After(d) => sleep_ms(min(d, pi.interval).num_milliseconds() as u32),
|
||||
},
|
||||
@@ -264,13 +264,13 @@ impl<D, S, C> GetToken for Authenticator<D, S, C>
|
||||
S: TokenStorage,
|
||||
C: BorrowMut<hyper::Client> {
|
||||
|
||||
/// Blocks until a token was retrieved from storage, from the server, or until the delegate
|
||||
/// Blocks until a token was retrieved from storage, from the server, or until the delegate
|
||||
/// decided to abort the attempt, or the user decided not to authorize the application.
|
||||
/// In any failure case, the delegate will be provided with additional information, and
|
||||
/// In any failure case, the delegate will be provided with additional information, and
|
||||
/// the caller will be informed about storage related errors.
|
||||
/// Otherwise it is guaranteed to be valid for the given scopes.
|
||||
fn token<'b, I, T>(&mut self, scopes: I) -> Result<Token, Box<Error>>
|
||||
where T: AsRef<str> + Ord,
|
||||
where T: AsRef<str> + Ord + 'b,
|
||||
I: IntoIterator<Item=&'b T> {
|
||||
let (scope_key, scopes) = {
|
||||
let mut sv: Vec<&str> = scopes.into_iter()
|
||||
@@ -292,12 +292,12 @@ impl<D, S, C> GetToken for Authenticator<D, S, C>
|
||||
let mut rf = RefreshFlow::new(self.client.borrow_mut());
|
||||
loop {
|
||||
match *rf.refresh_token(self.flow_type,
|
||||
&self.secret.client_id,
|
||||
&self.secret.client_secret,
|
||||
&self.secret.client_id,
|
||||
&self.secret.client_secret,
|
||||
&t.refresh_token) {
|
||||
RefreshResult::Error(ref err) => {
|
||||
match self.delegate.connection_error(err) {
|
||||
Retry::Abort|Retry::Skip =>
|
||||
Retry::Abort|Retry::Skip =>
|
||||
return Err(Box::new(StringError::new(
|
||||
err.description().to_string(),
|
||||
None))),
|
||||
@@ -306,7 +306,7 @@ impl<D, S, C> GetToken for Authenticator<D, S, C>
|
||||
},
|
||||
RefreshResult::RefreshError(ref err_str, ref err_description) => {
|
||||
self.delegate.token_refresh_failed(&err_str, &err_description);
|
||||
let storage_err =
|
||||
let storage_err =
|
||||
match self.storage.set(scope_key, &scopes, None) {
|
||||
Ok(_) => String::new(),
|
||||
Err(err) => err.to_string(),
|
||||
@@ -339,7 +339,7 @@ impl<D, S, C> GetToken for Authenticator<D, S, C>
|
||||
Ok(None) => {
|
||||
// Nothing was in storage - get a new token
|
||||
// get new token. The respective sub-routine will do all the logic.
|
||||
match
|
||||
match
|
||||
match self.flow_type {
|
||||
FlowType::Device => self.retrieve_device_token(&scopes),
|
||||
}
|
||||
@@ -387,13 +387,13 @@ impl<D, S, C> GetToken for Authenticator<D, S, C>
|
||||
|
||||
|
||||
/// A partially implemented trait to interact with the `Authenticator`
|
||||
///
|
||||
///
|
||||
/// The only method that needs to be implemented manually is `present_user_code(...)`,
|
||||
/// as no assumptions are made on how this presentation should happen.
|
||||
pub trait AuthenticatorDelegate {
|
||||
|
||||
/// Called whenever there is an HttpError, usually if there are network problems.
|
||||
///
|
||||
///
|
||||
/// Return retry information.
|
||||
fn connection_error(&mut self, &hyper::Error) -> Retry {
|
||||
Retry::Abort
|
||||
@@ -402,7 +402,7 @@ pub trait AuthenticatorDelegate {
|
||||
/// Called whenever we failed to retrieve a token or set a token due to a storage error.
|
||||
/// You may use it to either ignore the incident or retry.
|
||||
/// This can be useful if the underlying `TokenStorage` may fail occasionally.
|
||||
/// if `is_set` is true, the failure resulted from `TokenStorage.set(...)`. Otherwise,
|
||||
/// if `is_set` is true, the failure resulted from `TokenStorage.set(...)`. Otherwise,
|
||||
/// it was `TokenStorage.get(...)`
|
||||
fn token_storage_failure(&mut self, is_set: bool, _: &Error) -> Retry {
|
||||
let _ = is_set;
|
||||
@@ -417,11 +417,11 @@ pub trait AuthenticatorDelegate {
|
||||
/// Given `DateTime` is the expiration date
|
||||
fn expired(&mut self, &DateTime<UTC>) {}
|
||||
|
||||
/// Called if the user denied access. You would have to start over.
|
||||
/// Called if the user denied access. You would have to start over.
|
||||
/// This will be the last call the delegate receives.
|
||||
fn denied(&mut self) {}
|
||||
|
||||
/// Called if we could not acquire a refresh token for a reason possibly specified
|
||||
/// Called if we could not acquire a refresh token for a reason possibly specified
|
||||
/// by the server.
|
||||
/// This call is made for the delegate's information only.
|
||||
fn token_refresh_failed(&mut self, error: &String, error_description: &Option<String>) {
|
||||
@@ -431,7 +431,7 @@ pub trait AuthenticatorDelegate {
|
||||
|
||||
/// Called as long as we are waiting for the user to authorize us.
|
||||
/// Can be used to print progress information, or decide to time-out.
|
||||
///
|
||||
///
|
||||
/// If the returned `Retry` variant is a duration.
|
||||
/// # Notes
|
||||
/// * Only used in `DeviceFlow`. Return value will only be used if it
|
||||
@@ -446,7 +446,7 @@ pub trait AuthenticatorDelegate {
|
||||
/// * Will be called exactly once, provided we didn't abort during `request_code` phase.
|
||||
/// * Will only be called if the Authenticator's flow_type is `FlowType::Device`.
|
||||
fn present_user_code(&mut self, pi: &PollInformation) {
|
||||
println!("Please enter {} at {} and grant access to this application",
|
||||
println!("Please enter {} at {} and grant access to this application",
|
||||
pi.user_code, pi.verification_url);
|
||||
println!("Do not close this application until you either denied or granted access.");
|
||||
println!("You have time until {}.", pi.expires_at.with_timezone(&Local));
|
||||
@@ -494,4 +494,4 @@ mod tests {
|
||||
_ => panic!("Expected to retrieve token in one go"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user