My app should create an event in users calendar.
I have refresh token for all user I want to create a new event in their calendar.
I use NuGet package for google calendar client, but when I init google calendar client I can to init only single refresh token.
var token = new TokenResponse { RefreshToken = "single token only" };
var credentials = new UserCredential(new
GoogleAuthorizationCodeFlow(
new GoogleAuthorizationCodeFlow.Initializer {
ClientSecrets = new ClientSecrets() {
ClientId = "id",
ClientSecret = "secret"
}
}
),
"user",
token
);
var calenderClient = new CalendarService(new BaseClientService.Initializer() {
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
how can I add another user refresh token?
Thanks
This is how the api works. You get a refresh token for each user you authenticated for that user and then you can add something to their calendar. When you want to do the same for another user you will need to authenticate using the refresh token for that user and then add the event.
Your code looks good just loop it for each refresh token.
Related
I would like to get the email address of a user after successful sign-in. Google Plus APIs will be depreciated by Google. Any other way to access just email address of the user? Using the below code, I'll have access access token and id_token of the user.
UserCredential credential =
GoogleWebAuthorizationBroker.AuthorizeAsync(
new ClientSecrets { ClientId = clientId, ClientSecret = clientSecret }
, scopes
, userName
, CancellationToken.None
, new FileDataStore(fileDataStorePath)).Result;
Update: The following code worked.
var gmailService = new Google.Apis.Gmail.v1.GmailService(new BaseClientService.Initializer
{
HttpClientInitializer = credential,
ApplicationName = "App name"
});
var gmailProfile = gmailService.Users.GetProfile("me").Execute();
string EmailAddress = gmailProfile.EmailAddress;
Make sure that you have include the "email" scope as part of your scopes.
create a service object.
var service = new PeopleService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Peopleservice Oauth2 Authentication Sample"
});
Then make a request to the people api.
var results = service.People.Get("person/me").ExecuteAsync();
I am using this code and it works and allows my app to get the Calendar List from the used google account.
UserCredential credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
new ClientSecrets
{
ClientId = "xxx",
ClientSecret = "xxxx-xxxx",
},
new[] { CalendarService.Scope.Calendar },
"support#xxx.com",
CancellationToken.None).Result;
// Create the service.
var service = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "xxx",
});
CalendarListResource.ListRequest cal = service.CalendarList.List();
cal.MaxResults = 10;
var calresult = cal.Execute().Items;
My question is when i do this i than import all the Calender's And Events to my local database so after an hour i need to run a script to update the Database with the latest Calendar and Event info as they might have added new Events or deleted some same goes with Calender's.
Not sure where to look for any suggestion?
For others:
The answer is here:
How to access users calendar list and events from google api using access token and refresh token
Basically you save the token and later user it for that Calendar.
How can I Create/Edit users in an ASP.NET Core web app that use OAuth or OpenID Connect to authentication?
All the documentation and examples I have found allow the users to sign-up.
e.g.(active-directory-dotnet-webapp-openidconnect-aspnetcore)
The requirements I have are the ability to create/edit users and assign roles in our database AND then allow those users to login to the web app using Azure AD.
If you are building an app which may include azure ad user management , and want to create/edit users after admin user login . You could firstly refer to below code sample about how to call a web API in an ASP.NET Core web application using Azure AD :
https://github.com/Azure-Samples/active-directory-dotnet-webapp-webapi-openidconnect-aspnetcore
Then you could use Azure AD graph api to create azure ad users :
Firstly register the app in azure portal , setting redirect url(https://localhost:44371/signin-oidc for example) , add a key ,configure permissions for your application , To use azure ad graph api , you need to choose Windows Azure Active Directory ,and set delegate permission Read and write directory data(require admin consent) .
In the controller action(HttpPost) , you could use below code to create a user :
AuthenticationResult result = null;
try
{
string userObjectID = (User.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier"))?.Value;
AuthenticationContext authContext = new AuthenticationContext(Startup.Authority, new NaiveSessionCache(userObjectID, HttpContext.Session));
ClientCredential credential = new ClientCredential(Startup.ClientId, Startup.ClientSecret);
result = await authContext.AcquireTokenSilentAsync("https://graph.windows.net", credential, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));
var userData = new
{
accountEnabled = true,
displayName = "nan yu",
mailNickname = "nanyu",
passwordProfile = new
{
password = "xxxxxx",
forceChangePasswordNextLogin = false
},
userPrincipalName = "nanyuTest54#testbasic1.onmicrosoft.com"
};
// Forms encode todo item, to POST to the Azure AD graph api.
HttpContent content = new StringContent(JsonConvert.SerializeObject(userData), System.Text.Encoding.UTF8, "application/json");
//
// Add the azure ad user.
//
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "https://graph.windows.net/myorganization/users?api-version=1.6");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
request.Content = content;
HttpResponseMessage response = await client.SendAsync(request);
//
// Return user in the view.
//
if (response.IsSuccessStatusCode)
{
return RedirectToAction("Index");
}
else
{
//
// If the call failed with access denied, then drop the current access token from the cache,
// and show the user an error indicating they might need to sign-in again.
//
if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
}
}
}
catch (Exception ee)
{
//
// The user needs to re-authorize. Show them a message to that effect.
//
}
If i misunderstand your requirement , please feel free to let me know .
I try to get OpenID Connect running... A user of my Web API managed to get an Authorization Code of a OpenID Connect Provider. How am I supposed to pass this code to my ASP.NET Web API? How do I have to configure OWIN Middleware such that I can get an Access Token using the Authorization Code?
UPDATE:
A SPA uses AJAX for communicating with my web service (ASP.NET Web API). In my web service a use OWIN Middleware. I set OpenIDConnect as the authentication mechanism. When the web service is called for the first time it successfully redirected the user to the login page of the OpenID Connect Provider. The user could login and got an Authorization Code as a result. AFAIK this code could now be used (by my web service) to the an Access Token. However, I don't know how to get this code back to my web service (is this done using a header?) and then what to configure to get the Access Token. I guess I could call the token endpoint manually but I would like to take advantage of the OWIN component instead.
BenV already answered the question, but there's more to consider.
class partial Startup
{
public void ConfigureAuth(IAppBuilder app)
{
// ...
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
Notifications = new OpenIdConnectAuthenticationNotifications() {
AuthorizationCodeReceived = (context) => {
string authorizationCode = context.Code;
// (tricky) the authorizationCode is available here to use, but...
return Task.FromResult(0);
}
}
}
}
}
Two problems:
First of all, authorizationCode will get expired quickly. There's no sense in storing it.
The second problem is that AuthorizationCodeReceived event will not get fired for any of the page reloads as long as authorizationCode is not expired and stored inside the session.
What you need to do is to call AcquireTokenByAuthorizationCodeAsync which will cache it and handle properly inside TokenCache.DefaultShare:
AuthorizationCodeReceived = (context) => {
string authorizationCode = context.Code;
AuthenticationResult tokenResult = await context.AcquireTokenByAuthorizationCodeAsync(authorizationCode, new Uri(redirectUri), credential);
return Task.FromResult(0);
}
Now, before every call to the resource, invoke AcquireTokenSilentAsync to get the accessToken (it will use TokenCache or silently use refreshToken ). If token is expired, it will raise AdalSilentTokenAcquisitionException exception (invoke access code renew procedure).
// currentUser for ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier")
AuthenticationResult authResult = await context.AcquireTokenSilentAsync(resourceUri, credential, currentUser);
Calling AcquireTokenSilentAsync is very fast if token is cached.
Looks like the recommended approach is to use the AuthorizationCodeReceived event to exchange the Auth code for an Access Token. Vittorio has a blog entry that outlines the overall flow.
Here's an example from this sample app on GitHub of the Startup.Auth.cs code to set this up:
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = Authority,
Notifications = new OpenIdConnectAuthenticationNotifications()
{
AuthorizationCodeReceived = (context) =>
{
var code = context.Code;
ClientCredential credential = new ClientCredential(clientId, appKey);
string tenantID = context.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
string signedInUserID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
AuthenticationContext authContext = new AuthenticationContext(string.Format("https://login.windows.net/{0}", tenantID), new EFADALTokenCache(signedInUserID));
AuthenticationResult result = authContext.AcquireTokenByAuthorizationCode(
code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, graphResourceID);
return Task.FromResult(0);
},
...
}
Note: The AuthorizationCodeReceived event is invoked only once when authorization really takes place. If the auth code is already generated and stored, this event is not invoked. You have to logout or clear cookies to force this event to take place.
This is now built into Microsoft.Owin 4.1.0 or later. You can use SaveTokens to make the id_token available and then RedeemCode to get an access token and make that available
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
ClientSecret = "redacted",
RedirectUri = postLogoutRedirectUri,
//This allows multitenant
//https://github.com/Azure-Samples/guidance-identity-management-for-multitenant-apps/blob/master/docs/03-authentication.md
TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false
},
Notifications = new OpenIdConnectAuthenticationNotifications()
{
AuthenticationFailed = (context) =>
{
return Task.FromResult(0);
}
},
SaveTokens = true,
// Required for the authorization code flow to exchange for tokens automatically
// using this means you will need to provide RedirectUri and ClientSecret
RedeemCode = true
}
);
You can then access the tokens through the HttpContext object
var result = Request.GetOwinContext().Authentication.AuthenticateAsync("Cookies").GetAwaiter().GetResult();
string idToken = result.Properties.Dictionary["id_token"];
string accessToken = result.Properties.Dictionary["access_token"];
Sources:
How to get access token from httpcontext using owin and Mvc 5
You need to bypass the default owin validation to do custom Authorization:
new OpenIdConnectAuthenticationOptions
{
...,
TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters
{
ValidateIssuer = false
},
TokenValidationParameters = new TokenValidationParameters { NameClaimType = "name", RoleClaimType = ClaimTypes.Role },
This line of code solved my issue. We need to validate the issuer to be false.
I need to be able to use a refresh token to be able to re-authenticate a token after the access token has expired. How can I do this using the C# v3 API? I've looked at the UserCredential class and AuthorizationCodeFlow class and nothing is jumping out at me.
I'm using the following code to authenticate it originally.
var result = await new AuthorizationCodeMvcApp(this, new AppFlowMetadata()).
AuthorizeAsync(CancellationToken.None);
if (result.Credential != null)
{
var service = new YouTubeService(new BaseClientService.Initializer
{
HttpClientInitializer = result.Credential,
ApplicationName = "YouTube Upload Tool"
});
}
And this is my AppFlowMetadata class.
public class AppFlowMetadata : FlowMetadata
{
private static readonly IAuthorizationCodeFlow flow =
new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = "ID",
ClientSecret = "SECRET",
},
Scopes = new[] { YouTubeService.Scope.YoutubeUpload },
DataStore = new EFDataStore(-1) // A data store I implemented using Entity Framework 6.
});
public override string GetUserId(Controller controller)
{
return "test";
}
public override IAuthorizationCodeFlow Flow
{
get { return flow; }
}
}
If anyone can suggest anything, I would greatly appreciate it. Thank you.
While this is not an answer, this is how I got around it. I had to create the GET request for authorisation (redirect your user to the url you get back and set your Controller Action to receive the callback specified in your Google Developer Console) and the PUT request for the Token (which I then stored using EF6) manually. I used System.Net.Http.HttpClient to make these requests, which was quite straight forward. See this link for all the details I needed to get this working.
It was the only way I could set the access_type to "offline". If the .NET API does this, I'm still curious to find out how.
With the token data stored, I now use the API to validate and refresh the token when I need to. I actually did this in a server side console application rather than a MVC app (hence the EF token persistence).
UserCredential credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
new ClientSecrets
{
ClientId = "ID",
ClientSecret = "Secret"
},
new[] { YouTubeService.Scope.YoutubeUpload },
"12345",
CancellationToken.None,
new EFDataStore(-1) // My own implementation of IDataStore
);
// This bit checks if the token is out of date,
// and refreshes the access token using the refresh token.
if(credential.Token.IsExpired(SystemClock.Default))
{
if (!await credential.RefreshTokenAsync(CancellationToken.None))
{
Console.WriteLine("No valid refresh token.");
}
}
var service = new YouTubeService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "MY App"
});
I hope this helps others.