Google client API - limit oauth authentication to my domain - oauth-2.0

Has anyone had any experience of using the Google Client API to authorise against their domain by restricting the domain a user can login with?
The titbit that is required appears to be a qs parameter: hd='[Domain name]'
but there's nothing similar in the OAuth2Parameters parameters object
var oap = new OAuth2Parameters
{
AccessToken = Current == null ? null : Current.AccessToken,
RefreshToken = Current == null ? null : Current.RefreshToken,
ClientId = GoogleClientId,
ClientSecret = GoogleClientSecret,
Scope = "https://spreadsheets.google.com/feeds https://docs.google.com/feeds https://www.googleapis.com/auth/userinfo.email",
RedirectUri = HttpContext.Current.Request.Url.Scheme.Concatenate("://", HttpContext.Current.Request.Url.Authority, "/Builder/Authentication/Receive"),
AccessType = "offline" //ensures a refresh token (tho not currently working),
*HD = //Hmm if only... :(((*
};
var authorizationUrl = OAuthUtil.CreateOAuth2AuthorizationUrl(oap);
return Redirect(authorizationUrl);

so,in fact, all we need is to adjust the url thus:
var authorizationUrl = OAuthUtil.CreateOAuth2AuthorizationUrl(oap);
authorizationUrl += "&hd=" + "mydomain.com".UrlEncode();
return Redirect(authorizationUrl);
Hope that helps someone down the line.

Use hd parameter.
Google documentation
Warning: This tag is documented in OAuth 1.0 API Reference. In version 2 is not documented but works.
Important: OAuth 1.0 has been officially deprecated as of April 20,
2012. It will continue to work as per our deprecation policy, but we encourage you to migrate to OAuth 2.0 as soon as possible.

Related

Mailkit Can't authenticate with O365 oAuth2 account

I tried to authenticate on a O365 application I created on the Azure portal and it doesn't work as expected.
The following code works well but it's using a login/password and it's not recommended by Microsoft. (found here https://github.com/jstedfast/MailKit/issues/989)
var scopes = new[] { "https://outlook.office365.com/IMAP.AccessAsUser.All" };
var confidentialClientApplication = PublicClientApplicationBuilder.Create(_clientId).WithAuthority(AadAuthorityAudience.AzureAdMultipleOrgs).Build();
SecureString securePassword = new NetworkCredential("", _userPassword).SecurePassword;
var acquireTokenByUsernamePasswordParameterBuilder = confidentialClientApplication.AcquireTokenByUsernamePassword(scopes, _userMail, securePassword);
var authenticationResult = acquireTokenByUsernamePasswordParameterBuilder.ExecuteAsync().Result;
if (_debugCall)
{
imapClient = new ImapClient(new ProtocolLogger(_configurationId + "_IMAP_" + DateTime.Now.ToString("yyyyMMddHHssffff") + ".log"));
}
else
{
imapClient = new ImapClient();
}
imapClient.CheckCertificateRevocation = false;
imapClient.ServerCertificateValidationCallback = (s, c, h, e) => true;
imapClient.Connect(_webServiceUrl, _webServicePort, SecureSocketOptions.Auto);
imapClient.Authenticate(new SaslMechanismOAuth2(_userMail, authenticationResult.AccessToken));
if (string.IsNullOrEmpty(_folder))
{
oFolder = imapClient.Inbox;
}
else
{
oFolder = imapClient.GetFolder(_folder);
}
oFolder.Open(FolderAccess.ReadWrite);
In fact I want to be able to authenticate using the tenanid, client secret and clientid but without the interactive mode (as the app is a windows services).
So I tried to use another code with the tenantid, clientSecret and ClientId but I receive the "Authentication failed" error message :
var confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create(_clientId)
.WithClientSecret(_clientSecret)
.WithRedirectUri("http://localhost")
.WithAuthority(new Uri("https://login.microsoftonline.com/" + _tenantid + "/"))
.Build();
var scopes = new[] { "https://outlook.office365.com/.default" };
var authenticationResult = confidentialClientApplication.AcquireTokenForClient(scopes);
var authToken = authenticationResult.ExecuteAsync().Result;
var oauth2 = new SaslMechanismOAuth2(_userMail, authToken.AccessToken);
imapClient = new ImapClient(new ProtocolLogger("TEST_IMAP_" + DateTime.Now.ToString("yyyyMMddHHssffff") + ".log"));
imapClient.CheckCertificateRevocation = false;
imapClient.ServerCertificateValidationCallback = (s, c, h, e) => true;
imapClient.Connect(_webServiceUrl, _webServicePort, SecureSocketOptions.Auto);
imapClient.Authenticate(oauth2);
I've the following permission for my app on the Azure portal:
MSGraph
IMAP.AccessAsUser.All
Mail.Read
Mail.ReadWrite
Mail.Send
Did I miss something? I'm afraid it may be impossible? The official sample on Mailkit website use the interactive mode.
Btw, I'm using Mailkit v2.4
Thank you for your help.
It appears that OAUTH2 authentication with Office365 via the non-interactive method is unsupported by the Microsoft Exchange IMAP/POP3/SMTP protocols and that the only way to get access to Office365 mail using the non-interactive method of OAUTH2 authentication is via the Microsoft.Graph API.
I've been getting a lot of questions about this over the past few months and as far as I'm aware, no one (myself included) has been able to find a way to make this work.
I keep hoping to see someone trying to do this (even in another language) here on StackOverflow with an accepted answer. So far, all I've seen are questions about OAuth2 using the interactive approach (which, as you've seen, I have written documentation for and is known to work well with MailKit).

Google OAuth Returning Additional Scopes Without Requesting

I was testing around with Google's oauth and was trying out different scopes.
However, I then reduced my scope request to just this : "https://www.googleapis.com/auth/userinfo.email"
The following is more in dotnetcore
Dictionary<string, string> queries = new Dictionary<string, string>();
queries.Add("scope", "https://www.googleapis.com/auth/userinfo.email");
queries.Add("access_type", "offline");
queries.Add("include_granted_scopes" ,"true");
queries.Add("response_type", "code");
queries.Add("state", "state");
queries.Add("redirect_uri", "http://localhost:5000/api/authenticate/googauth");
queries.Add("client_id", _clientId);
queries.Add("prompt", "consent");
UriBuilder builder = new UriBuilder();
builder.Host = "accounts.google.com";
builder.Scheme = "https";
builder.Path = "o/oauth2/v2/auth";
//builder.Query = ""
foreach (var query in queries)
{
if (string.IsNullOrEmpty(builder.Query))
{
builder.Query += $"{query.Key}={query.Value}";
}
else
{
builder.Query += $"&{query.Key}={query.Value}";
}
}
var redirectUri = builder.Uri.ToString();
return Redirect(redirectUri);
From the returned code, I then retrieved the access token etc.
Dictionary<string, string> values = new Dictionary<string, string>();
values.Add("code", code);
values.Add("client_id", _clientId);
values.Add("client_secret",_clientSecret);
values.Add("redirect_uri", "http://localhost:5000/api/authenticate/googauth");
values.Add("grant_type", "authorization_code");
var client = new HttpClient();
var result = await client.PostAsync("https://oauth2.googleapis.com/token", new FormUrlEncodedContent(values));
var content = await result.Content.ReadAsStringAsync();
var convertedContent = JsonSerializer.Deserialize<GoogleAccesstoken>(content);
However, I seem to get more than what I asked for. I get this in the returned scopes :
openid https://www.googleapis.com/auth/user.gender.read https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/user.birthday.read
I've tried using incognito, and different browsers and they all return the same thing (thinking that it may have been a cache issue).
Is anyone able to help me on this?
Thanks.
Enables applications to use incremental authorization to request access to additional scopes in context. If you set this parameter's value to true and the authorization request is granted, then the new access token will also cover any scopes to which the user previously granted the application access. See the incremental authorization section for examples.
extract from google documentation: https://developers.google.com/identity/protocols/oauth2/web-server
Basically means that the user has previously granted you the other scopes. Could have been through a login screen or something where you have used the same clientId

DotNetOpenAuth - UserAgentClient::ExchangeUserCredentialForToken - How to pass the redirect_uri parameter

Trying to retrieve the OAuth2.0 AccessToken using DotNetOpenAuth library, coded the same as below -
https://github.com/DotNetOpenAuth/DotNetOpenAuth/wiki/Security-scenarios
private static IAuthorizationState GetAccessTokenFromOwnAuthSvr()
{
var server = new AuthorizationServerDescription();
server.TokenEndpoint = new Uri("https://localhost/STS/OAuth/Token");
server.ProtocolVersion = DotNetOpenAuth.OAuth2.ProtocolVersion.V20;
var client = new UserAgentClient(server, clientIdentifier: "RP");
client.ClientCredentialApplicator =
ClientCredentialApplicator.PostParameter("data!");
var token = client.ExchangeUserCredentialForToken(
"Max Muster", "test123", new[] { "http://localhost/demo"});
return token;
}
This is not working as AuthZ Server returns error, complaining about the missing redirect_uri.
Going through few of the links here, saw that an AuthorizationState has the redirect_uri option, but could not figure out how to pass the AuthorizationState object in the ExchangeUserCredentialForToken request.
Is there a way to send the redirect_uri parameter or the AuthorizationState object as part of the ExchangeUserCredentialForToken request ?
Thanks in advance
--Abraham V K

Linq to Twitter - Bad Authentication data

I've the the latest version of Linq to Twitter (3.1.2), and I'm receiving the "Bad Authentication data" error with the code below:
var auth = new ApplicationOnlyAuthorizer
{
CredentialStore = new InMemoryCredentialStore
{
ConsumerKey = "xxxx",
ConsumerSecret = "xxxx"
}
};
using (var twitter = new TwitterContext(auth))
{
var users = twitter.User.Where(s => s.Type == UserType.Search && s.Query == "filter:verified").ToList();
}
I thought at first that it could be Twitter taking a while to accept my new credentials, but I used Twitter's OAuth tool with my keys, and they produced tokens without issue. Any ideas what I'm missing here?
I could not find a duplicate, as the code referenced # https://stackoverflow.com/questions/16387037/twitter-api-application-only-authentication-with-linq2twitter#= is no longer valid in the version I am running.
That query doesn't support Application-Only authorization. Here's the Twitter docs to that:
https://dev.twitter.com/rest/reference/get/users/search
Instead, you can use SingleUserAuthorizer, documented here:
https://github.com/JoeMayo/LinqToTwitter/wiki/Single-User-Authorization
Like this:
var auth = new SingleUserAuthorizer
{
CredentialStore = new SingleUserInMemoryCredentialStore
{
ConsumerKey = ConfigurationManager.AppSettings["consumerKey"],
ConsumerSecret = ConfigurationManager.AppSettings["consumerSecret"],
AccessToken = ConfigurationManager.AppSettings["accessToken"],
AccessTokenSecret = ConfigurationManager.AppSettings["accessTokenSecret"]
}
};
To find out what type of authorization is possible, you can visit the L2T wiki at:
https://github.com/JoeMayo/LinqToTwitter/wiki
and each API query and command has a link at the bottom of the page to the corresponding Twitter API documentation.

Verify OAuth Token on Twitter

I'm storing the oauth info from Twitter in a Flash Cookie after the user goes though the oauth process. Twitter says that this token should only expire if Twitter or the user revokes the app's access.
Is there a call I can make to Twitter to verify that my stored token has not been revoked?
All API methods that require authentication will fail if the access token expires. However the specific method to verify who the user is and that the access token is still valid is GET account/verify_credentials
This question may be old, but this one is for the googlers (like myself).
Here is the call to twitter using Hammock:
RestClient rc = new RestClient {Method = WebMethod.Get};
RestRequest rr = new RestRequest();
rr.Path = "https://api.twitter.com/1/account/verify_credentials.json";
rc.Credentials = new OAuthCredentials
{
ConsumerKey = /* put your key here */,
ConsumerSecret = /* put your secret here */,
Token = /* user access token */,
TokenSecret = /* user access secret */,
Type = OAuthType.AccessToken
};
rc.BeginRequest(rr, IsTokenValid);
Here is the response:
public void IsTokenValid(RestRequest request, RestResponse response, object userState)
{
if(response.StatusCode == HttpStatusCode.OK)
{
var user = userState;
Helper.SaveSetting(Constants.TwitterAccess, user);
}
else
{
Dispatcher.BeginInvoke(() => MessageBox.Show("This application is no longer authenticated "))
}
}
I always borrow solutions from SO, this is my first attempt at giving back, albeit quite late to the question.
When debugging manually:
curl \
--insecure https://api.twitter.com/1/account/verify_credentials.json?oauth_access_token=YOUR_TOKEN
I am using TwitterOAuth API and here is the code based on the accepted answer.
$connection = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET, $twitter_oauth_token, $twitter_oauth_secret);
$content = $connection->get("account/verify_credentials");
if($connection->getLastHttpCode() == 200):
// Connection works fine.
else:
// Not working
endif;

Resources