Well, after a lot of help from posts on this site and others I've finally got my .net application connected and authenticating to my google apps instance! Hooray! I can search and get user details to my hearts delight utilizing a service account that I've delegated access to.
That said, I can't seem to be able to update a user's password. I get no errors, no response, nothing - it just doesn't work.
Here's what I got so far, like i said, i get user details great, just can't change that password!
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography.X509Certificates;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
using Google.Apis.Admin.Directory.directory_v1;
using Google.Apis.Admin.Directory.directory_v1.Data;
using Google.Apis.Admin.Directory;
String serviceAccountEmail = "mybiglongserviceaccountemail#developer.gserviceaccount.com";
var certificate = new X509Certificate2(#"c:\path\to\my\p12key.p12", "mysecret", X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
User="myadminuser#mydomain.com",
Scopes = new[] { DirectoryService.Scope.AdminDirectoryUser }
}.FromCertificate(certificate));
var dirservice = new DirectoryService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "MyProjectName",
});
User user = dirservice.Users.Get("username#mydomain.com").Execute();
Console.WriteLine(" email: " + user.PrimaryEmail);
Console.WriteLine(" last login: " + user.LastLoginTime);
user.Password = "newpassword";
dirservice.Users.Update(user, "username#mydomain.com");
The last two lines are the ones I expect would change the users password - but it doesn't.
Any help here would be much appreciated! Thanks so much!
Nevermind!
I forgot the .Execute()
it works with .execute()
user.Password = "newpassword";
dirservice.Users.Update(user, "username#mydomain.com").Execute();
Thanks!
In case anyone wants the complete write-up I did about how to configure the project, developer console, service account etc it's right here!
http://blog.mwpreston.net/2014/10/17/google-admin-sdk-apis-and-net-integration-changing-a-users-password/
Related
I need to validate user credentials from external service, therefore I'm using the VirtualUser authentication.
BuildVirtualUser, checking for the roles to set to him, saving the user Profile and then login with that name.
I'm facing a problem, that everyday that i login, with the same credentials Sitecore creates a new user in Experience Profile.
What i need to change in my code to assure that, with virtual user login, Sitecore gets the old experience profile of the user?
I was thinking in creating the user in sitecore with same generic password. Instead of using the virtual user, and authenticate directly with sitecore. Is that correct?
Here's my code:
Sitecore.Security.Accounts.User user = Sitecore.Security.Authentication.AuthenticationManager.BuildVirtualUser(sitecoreUser, true);
string roleName = #"newRole\User";
Sitecore.Security.Accounts.Role demoRole = Sitecore.Security.Accounts.Role.FromName(roleName);
if (Sitecore.Security.Accounts.Role.Exists(roleName) && !demoRole.IsMember(user, true, false))
{
user.Roles.Add(Sitecore.Security.Accounts.Role.FromName(roleName));
}
user.Profile.Name = name;
user.Profile.Email = email;
user.Profile.FullName = fullname;
user.Profile.Save();
Sitecore.Security.Authentication.AuthenticationManager.Login(user.Name);
Tracker.Initialize();
Code looks fine, but you miss one important thing: to identify your user/contact.
You need to add next line of code:
Tracker.Current.Session.Identify(email);
Please check next link to find more information about how to identify contacts:
https://doc.sitecore.net/sitecore_experience_platform/setting_up__maintaining/xdb/contacts/identifying_contacts
Identity server is implemented and working well. Google login is working and is returning several claims including email.
Facebook login is working, and my app is live and requests email permissions when a new user logs in.
The problem is that I can't get the email back from the oauth endpoint and I can't seem to find the access_token to manually request user information. All I have is a "code" returned from the facebook login endpoint.
Here's the IdentityServer setup.
var fb = new FacebookAuthenticationOptions
{
AuthenticationType = "Facebook",
SignInAsAuthenticationType = signInAsType,
AppId = ConfigurationManager.AppSettings["Facebook:AppId"],
AppSecret = ConfigurationManager.AppSettings["Facebook:AppSecret"]
};
fb.Scope.Add("email");
app.UseFacebookAuthentication(fb);
Then of course I've customized the AuthenticateLocalAsync method, but the claims I'm receiving only include name. No email claim.
Digging through the source code for identity server, I realized that there are some claims things happening to transform facebook claims, so I extended that class to debug into it and see if it was stripping out any claims, which it's not.
I also watched the http calls with fiddler, and I only see the following (apologies as code formatting doesn't work very good on urls. I tried to format the querystring params one their own lines but it didn't take)
(facebook.com)
/dialog/oauth
?response_type=code
&client_id=xxx
&redirect_uri=https%3A%2F%2Fidentity.[site].com%2Fid%2Fsignin-facebook
&scope=email
&state=xxx
(facebook.com)
/login.php
?skip_api_login=1
&api_key=xxx
&signed_next=1
&next=https%3A%2F%2Fwww.facebook.com%2Fv2.7%2Fdialog%2Foauth%3Fredirect_uri%3Dhttps%253A%252F%252Fidentity.[site].com%252Fid%252Fsignin-facebook%26state%3Dxxx%26scope%3Demail%26response_type%3Dcode%26client_id%3Dxxx%26ret%3Dlogin%26logger_id%3Dxxx&cancel_url=https%3A%2F%2Fidentity.[site].com%2Fid%2Fsignin-facebook%3Ferror%3Daccess_denied%26error_code%3D200%26error_description%3DPermissions%2Berror%26error_reason%3Duser_denied%26state%3Dxxx%23_%3D_
&display=page
&locale=en_US
&logger_id=xxx
(facebook.com)
POST /cookie/consent/?pv=1&dpr=1 HTTP/1.1
(facebook.com)
/login.php
?login_attempt=1
&next=https%3A%2F%2Fwww.facebook.com%2Fv2.7%2Fdialog%2Foauth%3Fredirect_uri%3Dhttps%253A%252F%252Fidentity.[site].com%252Fid%252Fsignin-facebook%26state%3Dxxx%26scope%3Demail%26response_type%3Dcode%26client_id%3Dxxx%26ret%3Dlogin%26logger_id%3Dxxx
&lwv=100
(facebook.com)
/v2.7/dialog/oauth
?redirect_uri=https%3A%2F%2Fidentity.[site].com%2Fid%2Fsignin-facebook
&state=xxx
&scope=email
&response_type=code
&client_id=xxx
&ret=login
&logger_id=xxx
&hash=xxx
(identity server)
/id/signin-facebook
?code=xxx
&state=xxx
I saw the code parameter on that last call and thought that maybe I could use the code there to get the access_token from the facebook API https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow
However when I tried that I get a message from the API telling me the code has already been used.
I also tried to change the UserInformationEndpoint to the FacebookAuthenticationOptions to force it to ask for the email by appending ?fields=email to the end of the default endpoint location, but that causes identity server to spit out the error "There was an error logging into the external provider. The error message is: access_denied".
I might be able to fix this all if I can change the middleware to send the request with response_type=id_token but I can't figure out how to do that or how to extract that access token when it gets returned in the first place to be able to use the Facebook C# sdk.
So I guess any help or direction at all would be awesome. I've spent countless hours researching and trying to solve the problem. All I need to do is get the email address of the logged-in user via IdentityServer3. Doesn't sound so hard and yet I'm stuck.
I finally figured this out. The answer has something to do with Mitra's comments although neither of those answers quite seemed to fit the bill, so I'm putting another one here. First, you need to request the access_token, not code (authorization code) from Facebook's Authentication endpoint. To do that, set it up like this
var fb = new FacebookAuthenticationOptions
{
AuthenticationType = "Facebook",
SignInAsAuthenticationType = signInAsType,
AppId = ConfigurationManager.AppSettings["Facebook:AppId"],
AppSecret = ConfigurationManager.AppSettings["Facebook:AppSecret"],
Provider = new FacebookAuthenticationProvider()
{
OnAuthenticated = (context) =>
{
context.Identity.AddClaim(new System.Security.Claims.Claim("urn:facebook:access_token", context.AccessToken, ClaimValueTypes.String, "Facebook"));
return Task.FromResult(0);
}
}
};
fb.Scope.Add("email");
app.UseFacebookAuthentication(fb);
Then, you need to catch the response once it's logged in. I'm using the following file from the IdentityServer3 Samples Repository, which overrides (read, provides functionality) for the methods necessary to log a user in from external sites. From this response, I'm using the C# Facebook SDK with the newly returned access_token claim in the ExternalAuthenticationContext to request the fields I need and add them to the list of claims. Then I can use that information to create/log in the user.
public override async Task AuthenticateExternalAsync(ExternalAuthenticationContext ctx)
{
var externalUser = ctx.ExternalIdentity;
var claimsList = ctx.ExternalIdentity.Claims.ToList();
if (externalUser.Provider == "Facebook")
{
var extraClaims = GetAdditionalFacebookClaims(externalUser.Claims.First(claim => claim.Type == "urn:facebook:access_token"));
claimsList.Add(new Claim("email", extraClaims.First(k => k.Key == "email").Value.ToString()));
claimsList.Add(new Claim("given_name", extraClaims.First(k => k.Key == "first_name").Value.ToString()));
claimsList.Add(new Claim("family_name", extraClaims.First(k => k.Key == "last_name").Value.ToString()));
}
if (externalUser == null)
{
throw new ArgumentNullException("externalUser");
}
var user = await userManager.FindAsync(new Microsoft.AspNet.Identity.UserLoginInfo(externalUser.Provider, externalUser.ProviderId));
if (user == null)
{
ctx.AuthenticateResult = await ProcessNewExternalAccountAsync(externalUser.Provider, externalUser.ProviderId, claimsList);
}
else
{
ctx.AuthenticateResult = await ProcessExistingExternalAccountAsync(user.Id, externalUser.Provider, externalUser.ProviderId, claimsList);
}
}
And that's it! If you have any suggestions for simplifying this process, please let me know. I was going to modify this code to do perform the call to the API from FacebookAuthenticationOptions, but the Events property no longer exists apparently.
Edit: the GetAdditionalFacebookClaims method is simply a method that creates a new FacebookClient given the access token that was pulled out and queries the Facebook API for the other user claims you need. For example, my method looks like this:
protected static JsonObject GetAdditionalFacebookClaims(Claim accessToken)
{
var fb = new FacebookClient(accessToken.Value);
return fb.Get("me", new {fields = new[] {"email", "first_name", "last_name"}}) as JsonObject;
}
I am using Xamarin.Auth For Authenticate user. I want to Authenticate Facebook User..Please check i have defined scope in code true or not.
My code for Authenticate given Below:
var auth = new OAuth2Authenticator (clientId: App.Instance.OAuthSettings.ClientId,
scope:"publish_stream,user_about_me,read_stream,email",
authorizeUrl: new Uri (App.Instance.OAuthSettings.AuthorizeUrl),
redirectUrl: new Uri (App.Instance.OAuthSettings.RedirectUrl));
auth.Completed += (sender, eventArgs) =>
{
if (eventArgs.IsAuthenticated)
{
App.Instance.SuccessfulLoginAction.Invoke();
// Use eventArgs.Account to do wonderful things
App.Instance.SaveToken(eventArgs.Account.Properties["access_token"]);
}
else
{
}
};
Please Help me Solve this Issue. i have attached image . please tell me what i missing.Thanks in Advance.
Facebook error Page
I am using below piece of code to list all domain users in my simple Console application
var certificate = new X509Certificate2("D:\\3acf2c2008cecd33b43de27e30016a72e1482c41-privatekey.p12", "notasecret", X509KeyStorageFlags.Exportable);
var privateKey = certificate.Export(X509ContentType.Cert);
var provider = new AssertionFlowClient(GoogleAuthenticationServer.Description, certificate)
{
ServiceAccountId = "877926787679-b7fd15en1sh2oc65e164v90cfcvrfftq#developer.gserviceaccount.com",
Scope = DirectoryService.Scopes.AdminDirectoryUserReadonly.GetStringValue(),
ServiceAccountUser = "user1#05.mygbiz.com"
};
var auth = new OAuth2Authenticator<AssertionFlowClient>(provider, AssertionFlowClient.GetState);
DirectoryService dirService = new DirectoryService(new BaseClientService.Initializer()
{
Authenticator = auth,
ApplicationName = "My APP"
});
Users users = dirService.Users.List().Execute();
Execute() method errors out saying Bad Request.
Questions:
How to overcome this issue?
Does this Admin SDK support trial version of Google APP account?
I have updated service account Client ID in Google Console and also updated in Admin Console with below scopes
https://www.googleapis.com/auth/admin.directory.group
https://www.googleapis.com/auth/admin.directory.user
and also set API access check box. Do I missing something in settings?
Like JoBe said, you should include the domain parameter.
happy_user = service.users().list(domain='mydomain.com').execute()
This has worked for me.
For one of our FBA enabled SharePoint site, we need to access various web services. I know that we need to invoke Authentication.asmx before we make any other SP web service call.
How do I get the currently logged in user's username & password to pass to the Authentication.asmx service?
Thanks.
Update: I tried Marek's solution with a known username and password and got a 401 for Authentication.asmx. So probably some settings are off. The admin is looking into it.
MembershipUser user = Membership.GetUser();
string username = user.UserName;
string password = user.GetPassword();
Authentication auth = new Authentication();
auth.CookieContainer = new CookieContainer();
LoginResult result = auth.Login(username, password);
if (result.ErrorCode == LoginErrorCode.NoError)
{
CookieCollection cookies = auth.CookieContainer.GetCookies(new Uri(auth.Url));
Cookie authCookie = cookies[result.CookieName];
Lists lists = new Lists();
lists.CookieContainer = new CookieContainer();
lists.CookieContainer.Add(authCookie);
lists.GetListCollection();
}
However, depending on the settings of the membership provider (is password stored in plain text, encrypted or hashed? is it required to pass the security answer to get the password?) retrieving the password may be more difficult or even impossible and you will need to ask the user for it.