I am trying to write a native app to access a users google calendar. I am trying to use the example that google has provided to get authentication but it never seems to fire the authentication function
private void Window_Initialized(object sender, EventArgs e)
{
var provider = new NativeApplicationClient(
GoogleAuthenticationServer.Description);
provider.ClientIdentifier = "<My Client Id here>";
provider.ClientSecret = "<My Client Secret here";
var auth = new OAuth2Authenticator<NativeApplicationClient>(
provider, (p) => GetAuthorization(provider));
CalendarService service = new CalendarService();
CalendarsResource.GetRequest cr = service.Calendars.Get("{primary}");
if (cr.CalendarId != null)
{
Console.WriteLine("Fetching calendar");
//Google.Apis.Calendar.v3.Data.Calendar c =
service.Calendars.Get("{primary}").Fetch();
}
else
{
Console.WriteLine("Service not found");
}
}
Here is the code that I am using for Authentication. I never see the console writeline get published.
private static IAuthorizationState GetAuthorization(NativeApplicationClient arg)
{
Console.WriteLine("Authorization Requested");
IAuthorizationState state = new AuthorizationState(
new[] { CalendarService.Scopes.Calendar.GetStringValue() });
state.Callback = new Uri(NativeApplicationClient.OutOfBandCallbackUrl);
Uri authUri = arg.RequestUserAuthorization(state);
Process.Start(authUri.ToString());
// Request authorization from the user and get the code
string authCode = Console.ReadLine();
// Retrieve the access token by using the authorization code:
return arg.ProcessUserAuthorization(authCode, state);
}
Are there any better tutorials available or am I doing something wrong?
The example code is broken. To make the service use your authenticator, you need to connect it. In the example there is no association between the service and the authenticator. Create the service like this:
var service = new CalendarService(new BaseClientService.Initializer()
{
Authenticator = auth
};
Look at https://code.google.com/p/google-api-dotnet-client/ for better documentation/working code.
Check out the new docs. You'll need to replace
var auth = new OAuth2Authenticator<NativeApplicationClient>(
provider, (p) => GetAuthorization(provider));
with
AuthenticatorFactory.GetInstance().RegisterAuthenticator(
() => new OAuth2Authenticator(provider, GetAuthentication));
As the comment says, when you call var service = new CalendarService(), the previously registered authenticator will automatically be called.
I think the above ways are related to old version of Google calendar DLL. Does any one know about the documentation of new version of Google calendar i.e. V 1.8.1.82. Google never provides the good documentation for .NET developer.s
Related
I hit a wall trying to use a custom SSO in my apps so I figured I'd take a step back and ask questions here.
I implemented a RESTful Authentication API in .Net 5.0 (without Identity) by following this article.
I can call http://localhost:4001/api/auth/login with an email and a password to get a JWT in response
{
"token": "eyJhb<--removed jwt-->",
"errors": null
}
Is this a good approach? I didn't want to use something as bulky as Identity or as complicated as or OAuth.
How do I tie this up with a normal authentication "flow"? I know that you have to Challenge the identity provider while logging in, but I couldn't find any exemples on how to do it that would fit my situation. (Itried to implement a custom scheme with builder.AddJwtBearer("my_scheme", ... but this is where I hit a wall)
I managed to do what I wanted, so if anyone stumbles upon this:
Turns out that I was making this out to be super complicated when it's actually quite simple...
The point was to make things easy, so trying to create en authentication scheme was off topic.
Here is the final code:
using var client = new HttpClient() { BaseAddress = new Uri("http://localhost:4001/api/") };
try
{
var stringContent = new StringContent(JsonSerializer.Serialize(new { email = model.Email, password = model.Password }), Encoding.UTF8, "application/json");
var result = await client.PostAsync("auth/login", stringContent);
var jsonResponse = await result.Content.ReadAsStringAsync();
var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
var authResult = JsonSerializer.Deserialize<AuthResult>(jsonResponse, options);
var jwt = authResult.Token.Jwt;
var isValidJwt = JwtHelper.ValidateCurrentToken(jwt, _myExternalAuthSettings.RsaPub, _myExternalAuthSettings.Issuer);
if (isValidJwt)
{
HttpContext.Response.Cookies.Append("my_auth", jwt, new CookieOptions()
{
Domain = "localhost",
Expires = DateTimeOffset.FromUnixTimeSeconds(authResult.Token.UnixTimeExpiresAt)
});
// finish authenticating the user
}
// something failed
}
Note: I followed this article to be able to locally validate the token with the public key (the private key is used to create it).
Cheers
I created a universal app via Microsoft's AppStudio.
I tried adding Facebook authentication to the app by following the 'Scrumptious tutorial' (http://facebooksdk.net/docs/phone/tutorial/).
When I run the app on my phone, I can never get to the facebook login page, because the following line: await App.FacebookSessionClient.LoginAsync("user_about_me,read_stream");
always results in the following exception:
System.NotImplementedException: Not implemented
at Windows.Security.Authentication.Web.WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions options, Uri requestUri, Uri callbackUri)
at Facebook.Client.FacebookSessionClient.d__24.MoveNext()
The source of the exception is this call in the FacebookSessionClient.cs (facebook-client package):
var result = await WebAuthenticationBroker.AuthenticateAsync(options, startUri, endUri);
It seems that this function is not implemented for the phone. I am still wondering how it is possible that the turial, which refers to the exact same code would work.
It's not implemented for 8.1 yet. If you want to use Facebook authentication in 8.1 you can use the following approach:
In your App class:
private const string RedirectUrl = "https://www.facebook.com/connect/login_success.html";
private static readonly IReadOnlyCollection<string> Permissions = new[] { "email", "offline_access" };
protected override void OnActivated(IActivatedEventArgs args)
{
base.OnActivated(args);
var continuationActivatedEventArgs = args as IContinuationActivatedEventArgs;
if (continuationActivatedEventArgs == null)
return;
var webAuthenticationResult = ((WebAuthenticationBrokerContinuationEventArgs)continuationActivatedEventArgs).WebAuthenticationResult;
if (webAuthenticationResult.ResponseStatus == WebAuthenticationStatus.Success)
{
var facebookClient = new FacebookClient();
var result = facebookClient.ParseOAuthCallbackUrl(new Uri(webAuthenticationResult.ResponseData));
if (!result.IsSuccess)
{
// Process unsuccessful authentication
}
else
{
// Process successful authentication
var accessToken = result.AccessToken;
}
}
}
// Authentication method, this method should be invoked when you click Facebook authentication button
public void AuthenticateAndContinue()
{
var loginUrl = GetLoginUrl();
WebAuthenticationBroker.AuthenticateAndContinue(loginUrl, new Uri(RedirectUrl));
}
private Uri GetLoginUrl()
{
var parameters = new Dictionary<string, object>();
parameters["client_id"] = "YourFacebookApplicationId";
parameters["redirect_uri"] = RedirectUrl;
parameters["response_type"] = "token";
parameters["display"] = "touch";
parameters["mobile"] = true;
parameters["scope"] = String.Join(",", Permissions);
var facebookClient = new FacebookClient();
return facebookClient.GetLoginUrl(parameters);
}
I put everything in one place just for example purposes, it's better to separate fb authentication logic.
You can find this approach here MSDN Windows Phone 8.1 Web Authentication samples
Perhaps I am the only one that thinks Google's API documentation is awful but I've spent more time on this simple task than I wanted.
Currently my project is using a GDATA implementation to connect with the Google Calendar API v2. I followed this guide: http://www.codeproject.com/Articles/565032/Google-Calendar-Integration-in-ASP-NET-Create-ed
But I noticed that Google is deprecating version 2 of their API this fall. I am trying to figure out how I can connect to their version 3 API which appears to be using OAuth2.
After reading their documentation and searching the internet >:( - The problem I keep running into is EVERY sample, tutorial or youtube video I've come across that shows how to implement this involve the Google consent screen where the user clicks "Accept".
I've tried doing the following but honestly not sure if it's even the right direction?
// Register the authenticator. The Client ID and secret have to be copied from the API Access
// tab on the Google APIs Console.
var provider = new NativeApplicationClient(GoogleAuthenticationServer.Description);
provider.ClientIdentifier = "MY_CLIENT_ID";
provider.ClientSecret = "MY_CLIENT_SECRET";
// Create the service. This will automatically call the previously registered authenticator.
var service = new CalendarService();
My application doesn't need the user's account/consent (OAuth), I need to connect like I am currently in my code-behind.
So the question is how do I "upgrade" my current implementation to v3? Do I use OAuth, Service Account? I've found plenty of examples showing the v3 usages for how to retrieve events and insert them... but they all authenticate with a user consent screen on the front end.
Here is my current GData implementation...
public class GoogleGateway : IGoogleGateway
{
private readonly IRepository<UserSetting> _settingsRepository;
private Service _googleService;
private CalendarService _googleCalendar;
private Uri _calendarUri;
public GoogleGateway(IRepository<UserSetting> settingsRepository)
{
_settingsRepository = settingsRepository;
}
public IEnumerable<EventEntry> GetAllEvents(DateTime? startDate)
{
if (!Connect()) return new List<EventEntry>();
// Create the query object:
EventQuery query = new EventQuery();
query.Uri = _calendarUri;
if (startDate != null)
query.StartTime = startDate.Value;
// Tell the service to query:
EventFeed calFeed = _googleCalendar.Query(query);
return calFeed.Entries.Cast<EventEntry>();
}
public bool Connect()
{
var calSettings = _settingsRepository.Get().Where(x => x.Setting == "Calendar");
if (calSettings.Any())
{
var username = calSettings.First(x => x.Meta == "GoogleUsername").Value;
var password = calSettings.First(x => x.Meta == "GooglePassword").Value;
var calendarUri = new Uri(calSettings.First(x => x.Meta == "CalendarFeed").Value);
var applicationName = calSettings.First(x => x.Meta == "ApplicationName").Value;
_calendarUri = calendarUri;
//FeedQuery feedQuery = new FeedQuery();
_googleService = new Service("cl", applicationName);
_googleCalendar = new CalendarService(applicationName);
// Set your credentials:
_googleService.setUserCredentials(username, password);
_googleCalendar.setUserCredentials(username, password);
return true;
}
return false;
}
public void AddEvent(string title, string contents, string location, DateTime startTime, DateTime endTime)
{
if (!Connect()) return;
EventEntry.EVENT_CATEGORY = new AtomCategory("Appointments");
EventEntry entry = new EventEntry
{
Title = { Text = title },
Content = { Content = contents },
};
// Set the title and content of the entry.
// Set a location for the event.
Where eventLocation = new Where();
eventLocation.ValueString = location;
entry.Locations.Add(eventLocation);
When eventTime = new When(startTime, endTime);
entry.Times.Add(eventTime);
Uri postUri = new Uri("http://www.google.com/calendar/feeds/default/private/full");
// Send the request and receive the response:
AtomEntry insertedEntry = _googleCalendar.Insert(postUri, entry);
}
public void DeleteEvent(string eventId)
{
if (!Connect()) return;
var events = GetAllEvents(null);
var appointment = events.First(x => x.EventId == eventId);
_googleService.Delete(appointment);
}
}
I'm growing desperate at this point, any help would be very appreciated. Include your twitter handle in your answer and I'll buy you a coffee!
UPDATED
I currently have the following, but I is still not authenticating... :(
static CalendarService BuildService()
{
String serviceAccountEmail = "xxxxxxxxxxxxx-31xxxxxxxxxxxxxxxxxxx#developer.gserviceaccount.com";
var certPath = HttpContext.Current.Server.MapPath("/xxxxxxxxxxxx.p12");
var certificate = new X509Certificate2(certPath, "notasecret", X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = new[] { CalendarService.Scope.Calendar }
}.FromCertificate(certificate));
// Create the service.
var service = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential, <<<<<< DOES NOT RESOLVE!
ApplicationName = "MyApplication",
});
var test = service.Calendars.Get("xxxxxxxxxxxxxxxxxx#group.calendar.google.com");
return service;
}
The problem is that you are storing credentials in plaintext. In Oauth2 the users won't give you their credentials (thus access to everything) but instead they enable your app to access the data of a specific type / scope.
It's not clear from your description whether you only ever access one calendar fully in your control or you have multiple users. In the first case the answer would be use service account (https://developers.google.com/accounts/docs/OAuth2ServiceAccount). In the second case if you are a calendar app with many users, you will need to go down the user consent road and you should read on :)
For offline access you can specify that the access_type should be offline when retrieving the credentials for the first time. Together with the access token you'll also get a refresh token, which you can use to re-authenticate at any later time without any more user clicks (https://developers.google.com/accounts/docs/OAuth2WebServer#refresh). However, at least one consent screen it is.
This is my code
For some reason i am unable to update the status i am getting this error .
Your credentials do not allow access to this resource at
var tweet = twitterCtx.UpdateStatus("Hello world");
var auth = new ApplicationOnlyAuthorizer
{
Credentials = new InMemoryCredentials
{
ConsumerKey = "",
ConsumerSecret = ""
}
};
auth.Authorize();
//auth.Invalidate();
var twitterCtx = new TwitterContext(auth);
var tweet = twitterCtx.UpdateStatus("Hello world");
i checked my ConsumerKey and Secret are correct and also i gave my app read write acccess. I am able to get the previous status , User Name but i am just unable to tweet a new status
Application Only authorization can only perform operations that are application-level. This differs from other authorizers that let you operate on behalf of a user. The logic is that a user has an account, but an application doesn't. Therefore, you can't tweet on behalf of an application because the tweet can't be assigned anywhere. However, if you tweet on behalf of a user, the tweet goes into that user's list of statuses (their timeline).
LINQ to Twitter has various authorizers and you can see them in use by downloading samples specific to the technology you're using. The downloadable source code also has samples. Here's an example of how to use the PIN authorizer:
static ITwitterAuthorizer DoPinOAuth()
{
// validate that credentials are present
if (ConfigurationManager.AppSettings["twitterConsumerKey"].IsNullOrWhiteSpace() ||
ConfigurationManager.AppSettings["twitterConsumerSecret"].IsNullOrWhiteSpace())
{
Console.WriteLine("You need to set twitterConsumerKey and twitterConsumerSecret in App.config/appSettings. Visit http://dev.twitter.com/apps for more info.\n");
Console.Write("Press any key to exit...");
Console.ReadKey();
return null;
}
// configure the OAuth object
var auth = new PinAuthorizer
{
Credentials = new InMemoryCredentials
{
ConsumerKey = ConfigurationManager.AppSettings["twitterConsumerKey"],
ConsumerSecret = ConfigurationManager.AppSettings["twitterConsumerSecret"]
},
AuthAccessType = AuthAccessType.NoChange,
UseCompression = true,
GoToTwitterAuthorization = pageLink => Process.Start(pageLink),
GetPin = () =>
{
// this executes after user authorizes, which begins with the call to auth.Authorize() below.
Console.WriteLine("\nAfter authorizing this application, Twitter will give you a 7-digit PIN Number.\n");
Console.Write("Enter the PIN number here: ");
return Console.ReadLine();
}
};
// start the authorization process (launches Twitter authorization page).
auth.Authorize();
return auth;
}
This method returns an instance of PinAuthorizer, auth, and you can use it something like this:
PinAuthorizer auth = DoPinAuth();
var ctx = new TwitterContext(auth);
ctx.UpdateStatus("Hello LINQ to Twitter!");
* Update *
The preceding code was from the old version of LINQ to Twitter. Here's an example of how to do it with the newer async version:
static IAuthorizer DoPinOAuth()
{
var auth = new PinAuthorizer()
{
CredentialStore = new InMemoryCredentialStore
{
ConsumerKey = ConfigurationManager.AppSettings["consumerKey"],
ConsumerSecret = ConfigurationManager.AppSettings["consumerSecret"]
},
GoToTwitterAuthorization = pageLink => Process.Start(pageLink),
GetPin = () =>
{
Console.WriteLine(
"\nAfter authorizing this application, Twitter " +
"will give you a 7-digit PIN Number.\n");
Console.Write("Enter the PIN number here: ");
return Console.ReadLine();
}
};
return auth;
}
And then you can use it like this:
var auth = DoPinOAuth();
await auth.AuthorizeAsync();
var twitterCtx = new TwitterContext(auth);
await twitterCtx.TweetAsync("Hello LINQ to Twitter!");
For more information, you can find Documentation and Source code. The source code has demos in the New\Demos folder.
There's a method on the TwitterContext named UpdateStatus, which you would use like this:
twitterCtx.UpdateStatus(" Your text goes here ");
You'll need to authorize with OAuth for this to work, so take the PIN option in the demo. After you're authorized, you can call UpdateStatus.
I have an application set up with Azure ACS and .net 4.5 using claims. My application uses dropbox also. I was wondering if i could let users identify them self with dropbox alone.
I get a token from dropbox when the user logs in with dropbox and a unique id. Where in the .net pipe do i tell it that i have authenticated a user, such the principals are set on the next request also.
To make the example simple, lets say i have a form with two inputs. name,pass. If the name is 1234 and pass is 1234. then i would like to tell the asp.net pipeline that the user is authenticated. Is this possible? or do i need to create custom token handlers an such to integrate it into WIF?
Update
I found this: I would like comments on the solution, if there are security concerns i should be aware off.
var sam = FederatedAuthentication.SessionAuthenticationModule;
if (sam != null)
{
var cp = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim> {new Claim("Provider","Dropbox")}, "OAuth"));
var transformer = FederatedAuthentication.FederationConfiguration.IdentityConfiguration.ClaimsAuthenticationManager;
if (transformer != null)
{
cp = transformer.Authenticate(String.Empty, cp);
}
var token = new SessionSecurityToken(cp);
sam.WriteSessionTokenToCookie(token);
}
All code:
public HttpResponseMessage get_reply_from_dropbox(string reply_from)
{
var response = this.Request.CreateResponse(HttpStatusCode.Redirect);
var q = this.Request.GetQueryNameValuePairs();
var uid = q.FirstOrDefault(k => k.Key == "uid");
if (!string.IsNullOrEmpty(uid.Value))
{
var sam = FederatedAuthentication.SessionAuthenticationModule;
if (sam != null)
{
var cp = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim> {new Claim("Provider","Dropbox")}, "OAuth"));
var transformer = FederatedAuthentication.FederationConfiguration.IdentityConfiguration.ClaimsAuthenticationManager;
if (transformer != null)
{
cp = transformer.Authenticate(String.Empty, cp);
}
var token = new SessionSecurityToken(cp);
sam.WriteSessionTokenToCookie(token);
}
}
response.Headers.Location = new Uri(reply_from);
return response;
}
public async Task<string> get_request_token_url(string reply_to)
{
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("OAuth",
string.Format("oauth_version=\"1.0\", oauth_signature_method=\"PLAINTEXT\", oauth_consumer_key=\"{0}\", oauth_signature=\"{1}&\"",
"<dropboxkey>","<dropboxsecret>"));
var data = await client.GetStringAsync("https://api.dropbox.com/1/oauth/request_token");
var pars = data.Split('&').ToDictionary(k=>k.Substring(0,k.IndexOf('=')),v=>v.Substring(v.IndexOf('=')+1));
return "https://www.dropbox.com/1/oauth/authorize?oauth_token=" + pars["oauth_token"]
+ "&oauth_callback=<MYSITE>/api/dropbox/get_reply_from_dropbox?reply_from=" + reply_to;
}
It works by the user request the authentication url, when the user authenticates my app it returns to get_reply_from_dropbox and logs in the user.
I offcause needs to handle some other stuff also, like what if the request do not come from dropbox.
I did this for my site using WIF 3.5 (not exactly the same) but it did use ACS+forms auth+OAuth all together, basically it uses form auth (which you can control completely) or use ACS/OAuth and link the accounts together or just use ACS/OAuth by itself.
You will have to handle logging off differently though.
http://garvincasimir.wordpress.com/2012/04/05/tutorial-mvc-application-using-azure-acs-and-forms-authentication-part-1/
DropBox uses OAuth, so I would go that route and then if you want to "link the accounts" create a user/password for forms auth linked to the DropBox Oauth account. The user doesn't necessarily have to know what auth conventions are being used. ASP.NET MVC 4 has the OAuth/forms auth built in the default project.