Clear Credentials stored in Universal Windows App - c#

I'm using Windows.Web.Http.HttpClient in Universal Windows platform (UWP). The URL needs domain credentials (NTLM) so windows opens a self defined popup for username and password. App needs a functionality to logout but I couldn;'t find a working code which can clear credentials stored by UWP.
I have tried to clear credentials from Windows.Security.Credentials.PasswordVault using following code but it didn't work:
var cred = new Windows.Security.Credentials.PasswordVault();
var pwds = cred.RetrieveAll();
foreach(var pwd in pwds)
{
pwd.RetrievePassword();
cred.Remove(pwd);
}
I'm also clearing cookies as below:
var filter = new HttpBaseProtocolFilter();
var cookieManager = filter.CookieManager;
HttpCookieCollection cookies = cookieManager.GetCookies(uri);
foreach (HttpCookie u in cookies)
{
cookieManager.DeleteCookie(u);
}
Any suggestions please?

This isn't available in Windows 10, but will be in the Anniversary Update:
var filter = new HttpBaseProtocolFilter();
filter.ClearAuthenticationCache();
You can see more on the MSDN page and if you have an Insider Preview build / SDK later than 14295 you should be able to test it.

Please look at:
https://learn.microsoft.com/en-us/windows/uwp/security/credential-locker#deleting-user-credentials
There function for deleting the credentials is described.
It seems that the method public IReadOnlyList<PasswordCredential> RetrieveAll() that you are using returns a read-only collection. Therefor its values can't be deleted.
Try to access the credentials e.g. with public PasswordCredential Retrieve(String resource, String userName). The return type which is not read-only, should enable you to use the delete methods.
If you want to delete all credentials for a specific resource name, this workaround works even in older Windows 10 versions:
private void RemoveAllCredentials(PasswordVault passwordVault)
{
//Get all credentials.
List<PasswordCredential> passwordCredentials = new List<PasswordCredential>();
var credentials = passwordVault.RetrieveAll();
foreach (PasswordCredential credential in credentials)
{
if (credential.Resource.Equals("ResourceName"))
{
passwordCredentials.Add(
passwordVault.Retrieve(credential.Resource, credential.UserName));
}
}
foreach (PasswordCredential entry in passwordCredentials)
{
passwordVault.Remove(entry);
}
}

Related

Token failed : 401 Forbidden Error connecting to Sharepoint using pnpframework

I am writing a web app that I would like to have access to Sharepoint Document Libraries from a particular site using the currently logged on user credentials. I have looked at a number of articles that suggest using the PnP Framework and using a certificate instead of the client/secret ids.
I have tried both, the code of which is below:
string siteCollectionURL = "https://mycompanyname.sharepoint.com/sites/staffportal";
var authManager = new AuthenticationManager(ApplicationId, "C:\\pathtopfxfile\certifcatefx.pfx", "certificatepassword", "https://mycompany.sharepoint.com/sites/staffportal");
using (var clientContext = authManager.GetACSAppOnlyContext(siteCollectionURL,ApplicationId,ClientSecretId))
{
clientContext.Load(clientContext.Web, p => p.Title);
clientContext.ExecuteQuery();
return Ok(clientContext.Web.Title);
}
Unfortunately on the ExecuteQuery line I am consistently getting the 401 error, indicating that I am not authorized.
I registered the app in Azure -> Enterprise applications:
I have checked the following articles:
How to use the PnP Framework in a c# application
Secure Authentication of SharePoint with PnP Framework with C#(Code)
And tried the code snippets, but I cannot seem to find anything that suggests using the currently logged in user to the Web app.(see screen shot) - the user is a global administrator
Below is the error:
Any suggestions would be much appreciated.
Regards,
Darren
UPDATE TO ARTICLE
jimas13's link is what pointed me in the right direction. The tweaks I mentioned in the comment I have posted below for anyone wanting to write an MVC C# web app. This does require that the app needs to be registered and needs a self-signed certificate setup.
The two Async lines need to be written as follows:
public static async Task<AuthenticationResult> GetToken(IConfidentialClientApplication app, string[] scopes)
{
return await app.AcquireTokenForClient(scopes).ExecuteAsync();
}
public static async Task<Web> GetClientContext(string Uri,AuthenticationResult authResult)
{
using (var clientContext = ContextHelper.GetClientContext(Uri, authResult.AccessToken))
{
Web web = clientContext.Web;
clientContext.Load(web);
await clientContext.ExecuteQueryAsync();
return web;
}
}
The rest of the code is here:
public IActionResult Index()
{
AuthenticationConfiguration.AuthenticationConfiguration config = AuthenticationConfiguration.AuthenticationConfiguration.ReadFromJsonFile("appsettings.json");
string siteURL = config.SiteUrl;
string[] scopes = new string[] { config.Scope };
CertificateDescription certificate = config.Certificate;
ICertificateLoader certificateLoader = new DefaultCertificateLoader();
certificateLoader.LoadIfNeeded(certificate);
IConfidentialClientApplication app = ConfidentialClientApplicationBuilder.Create(config.ClientId)
.WithCertificate(certificate.Certificate)
.WithTenantId(config.Tenant)
.WithAuthority(config.Authority)
.Build();
AuthenticationResult result = GetToken(app, scopes).Result;
Web WebSite = GetClientContext(siteURL, result).Result;
return Ok(WebSite.Title);
}
The .SiteUrl and .Scope were added to the AuthenticationConfiguration.cs file as a property and then also added to the appsettings.json file.

LibGit2Sharp: How to push a local repo commit to Azure DevOps remote repo using a Personal Access Token inside a custom HTTP authentication header?

I am trying to push a commit I made on my local repository to a remote counterpart, hosted on a private Azure DevOps server, using LibGit2Sharp programmatically.
As per the Azure documentation, the HTTPS OAuth enabled Personal Access Token needs to sent with the request in a custom Authentication header as 'Basic' with the Base64 encoded token:
var personalaccesstoken = "PATFROMWEB";
using (HttpClient client = new HttpClient()) {
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",
Convert.ToBase64String(Encoding.ASCII.GetBytes($":{personalaccesstoken}")));
using (HttpResponseMessage response = client.GetAsync(
"https://dev.azure.com/{organization}/{project}/_apis/build/builds?api-version=5.0").Result) {
response.EnsureSuccessStatusCode();
}
}
The LibGit2Sharp.CloneOptions class has a FetchOptions field which in turn has a CustomHeaders array that can be used to inject the authentication header during the clone operation, like the following (as mentioned in this issue):
CloneOptions cloneOptions = new() {
CredentialsProvider = (url, usernameFromUrl, types) => new UsernamePasswordCredentials {
Username = $"{USERNAME}",
Password = $"{ACCESSTOKEN}"
},
FetchOptions = new FetchOptions {
CustomHeaders = new[] {
$"Authorization: Basic {encodedToken}"
}
}
};
Repository.Clone(AzureUrl, LocalDirectory, cloneOptions);
And the clone process succeeds (I tested it as well as checked the source code :) )
However, the LibGit2Sharp.PushOptions does not have any such mechanism to inject authentication headers. I am limited to the following code:
PushOptions pushOptions = new()
{
CredentialsProvider = (url, usernameFromUrl, types) => new UsernamePasswordCredentials
{
Username = $"{USERNAME}",
Password = $"{PASSWORD}"
}
};
This is making my push operation fail with the following message:
Too many redirects or authentication replays
I checked the source code for Repository.Network.Push() on Github.
public virtual void Push(Remote remote, IEnumerable<string> pushRefSpecs, PushOptions pushOptions)
{
Ensure.ArgumentNotNull(remote, "remote");
Ensure.ArgumentNotNull(pushRefSpecs, "pushRefSpecs");
// Return early if there is nothing to push.
if (!pushRefSpecs.Any())
{
return;
}
if (pushOptions == null)
{
pushOptions = new PushOptions();
}
// Load the remote.
using (RemoteHandle remoteHandle = Proxy.git_remote_lookup(repository.Handle, remote.Name, true))
{
var callbacks = new RemoteCallbacks(pushOptions);
GitRemoteCallbacks gitCallbacks = callbacks.GenerateCallbacks();
Proxy.git_remote_push(remoteHandle,
pushRefSpecs,
new GitPushOptions()
{
PackbuilderDegreeOfParallelism = pushOptions.PackbuilderDegreeOfParallelism,
RemoteCallbacks = gitCallbacks,
ProxyOptions = new GitProxyOptions { Version = 1 },
});
}
}
As we can see above, the Proxy.git_remote_push method call inside the Push() method is passing a new GitPushOptions object, which indeed seems to have a CustomHeaders field implemented. But it is not exposed to a consumer application and is being instantiated in the library code directly!
It is an absolute necessity for me to use the LibGit2Sharp API, and our end-to-end testing needs to be done on Azure DevOps repositories, so this issue is blocking me from progressing further.
My questions are:
Is it possible to use some other way to authenticate a push operation on Azure from LibGit2Sharp? Can we leverage the PushOptions.CredentialsProvider handler so that it is compatible with the auth-n method that Azure insists on?
Can we cache the credentials by calling Commands.Fetch by injecting the header in a FetchOptions object before carrying out the Push command? I tried it but it fails with the same error.
To address the issue, is there a modification required on the library to make it compatible with Azure Repos? If yes, then I can step up and contribute if someone could give me pointers on how the binding to the native code is made :)
I will provide an answer to my own question as we have fixed the problem.
The solution to this is really simple; I just needed to remove the CredentialsProvider delegate from the PushOptions object, that is:
var pushOptions = new PushOptions();
instead of,
PushOptions pushOptions = new()
{
CredentialsProvider = (url, usernameFromUrl, types) => new UsernamePasswordCredentials
{
Username = $"{USERNAME}",
Password = $"{PASSWORD}"
}
};
¯\(ツ)/¯
I don't know why it works, but it does. (Maybe some folks from Azure can clarify it to us.)
It turns out that this works on windows (push options with no credentials provider). Perhaps because somewhere a native call the OS resolves the credentials using some other means. But in Linux / container environment, the issue persists.
"There was a problem pushing the repo: remote authentication required but no callback set"
I think as you mentioned, minimally the CustomHeaders implementation must be exposed for this to work.
Image of error on console

Authenticate to SharePoint Online C#

I'm trying to connect to SharePoint online in a console App and print the title of the site.
Its giving me the error : "The sign-in name or password does not match one in the Microsoft account system."
I have checked and made sure the username and password are 100% right.
I dont know what else to check
Heres my code:
private static void SPCredentialsConnect()
{
const string SiteUrl = "https://tenant.sharepoint.com/sites/mysite";
const string pwd = "appPassword";
const string username = "username#tenant.onmicrosoft.com";
SecureString securestring = new SecureString();
pwd.ToCharArray().ToList().ForEach(s => securestring.AppendChar(s));
ClientContext context = new ClientContext(SiteUrl);
context.Credentials = new SharePointOnlineCredentials(username, securestring);
try
{
var web = context.Web;
context.Load(web);
context.ExecuteQuery();
Console.WriteLine($"web title: {web.Title}");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
Have your issue fixed? “The sign-in name or password does not match one in the Microsoft account system” Error will occur sometimes and fixed after a while with nothing changed.
AppOnly Authentication for sharepointonline can't be registed in Azure Active Directory.
It should be register in
https://contoso.sharepoint.com/_layouts/15/appregnew.aspx
And grant permission in
https://contoso-admin.sharepoint.com/_layouts/15/appinv.aspx
You can refer to following document
https://learn.microsoft.com/en-us/sharepoint/dev/solution-guidance/security-apponly-azureacs
Consider using the PnP.Framework (a NuGet package), and use the AuthenticationManager object for SPO sites. This method bypasses MFA (which is mandatory in our organization, FWIW). You can find a lot more information and examples here, including steps on getting the client id and client secret for a site. Here is what we use to log into SPO sites:
using (ClientContext context =
new AuthenticationManager().GetACSAppOnlyContext(SiteUrl, clientID, clientSecret))
{
...
}
Also, once you connect, you should adjust the Context.Load to grab the title if you want to use that value right away. Here's what I used in my code:
context.Load(web, p => p.Id, p => p.Title);
context.ExecuteQuery();
Console.WriteLine($"Logged into source {web.Title} ({web.Id})");
Good luck!
Steve in Spain

Google+ API: How can I use RefreshTokens to avoid requesting access every time my app launches?

I'm trying to use the Google+ API to access info for the authenticated user. I've copied some code from one of the samples, which works fine (below), however I'm having trouble making it work in a way I can reuse the token across app-launches.
I tried capturing the "RefreshToken" property and using provider.RefreshToken() (amongst other things) and always get a 400 Bad Request response.
Does anyone know how to make this work, or know where I can find some samples? The Google Code site doesn't seem to cover this :-(
class Program
{
private const string Scope = "https://www.googleapis.com/auth/plus.me";
static void Main(string[] args)
{
var provider = new NativeApplicationClient(GoogleAuthenticationServer.Description);
provider.ClientIdentifier = "BLAH";
provider.ClientSecret = "BLAH";
var auth = new OAuth2Authenticator<NativeApplicationClient>(provider, GetAuthentication);
var plus = new PlusService(auth);
plus.Key = "BLAH";
var me = plus.People.Get("me").Fetch();
Console.WriteLine(me.DisplayName);
}
private static IAuthorizationState GetAuthentication(NativeApplicationClient arg)
{
// Get the auth URL:
IAuthorizationState state = new AuthorizationState(new[] { Scope });
state.Callback = new Uri(NativeApplicationClient.OutOfBandCallbackUrl);
Uri authUri = arg.RequestUserAuthorization(state);
// Request authorization from the user (by opening a browser window):
Process.Start(authUri.ToString());
Console.Write(" Authorization Code: ");
string authCode = Console.ReadLine();
Console.WriteLine();
// Retrieve the access token by using the authorization code:
return arg.ProcessUserAuthorization(authCode, state);
}
}
Here is an example. Make sure you add a string setting called RefreshToken and reference System.Security or find another way to safely store the refresh token.
private static byte[] aditionalEntropy = { 1, 2, 3, 4, 5 };
private static IAuthorizationState GetAuthorization(NativeApplicationClient arg)
{
// Get the auth URL:
IAuthorizationState state = new AuthorizationState(new[] { PlusService.Scopes.PlusMe.GetStringValue() });
state.Callback = new Uri(NativeApplicationClient.OutOfBandCallbackUrl);
string refreshToken = LoadRefreshToken();
if (!String.IsNullOrWhiteSpace(refreshToken))
{
state.RefreshToken = refreshToken;
if (arg.RefreshToken(state))
return state;
}
Uri authUri = arg.RequestUserAuthorization(state);
// Request authorization from the user (by opening a browser window):
Process.Start(authUri.ToString());
Console.Write(" Authorization Code: ");
string authCode = Console.ReadLine();
Console.WriteLine();
// Retrieve the access token by using the authorization code:
var result = arg.ProcessUserAuthorization(authCode, state);
StoreRefreshToken(state);
return result;
}
private static string LoadRefreshToken()
{
return Encoding.Unicode.GetString(ProtectedData.Unprotect(Convert.FromBase64String(Properties.Settings.Default.RefreshToken), aditionalEntropy, DataProtectionScope.CurrentUser));
}
private static void StoreRefreshToken(IAuthorizationState state)
{
Properties.Settings.Default.RefreshToken = Convert.ToBase64String(ProtectedData.Protect(Encoding.Unicode.GetBytes(state.RefreshToken), aditionalEntropy, DataProtectionScope.CurrentUser));
Properties.Settings.Default.Save();
}
The general idea is as follows:
You redirect the user to Google's Authorization Endpoint.
You obtain a short-lived Authorization Code.
You immediately exchange the Authorization Code for a long-lived Access Token using Google's Token Endpoint. The Access Token comes with an expiry date and a Refresh Token.
You make requests to Google's API using the Access Token.
You can reuse the Access Token for as many requests as you like until it expires. Then you can use the Refresh Token to request a new Access Token (which comes with a new expiry date and a new Refresh Token).
See also:
The OAuth 2.0 Authorization Protocol
Google's OAuth 2.0 documentation
I also had problems with getting "offline" authentication to work (i.e. acquiring authentication with a refresh token), and got HTTP-response 400 Bad request with a code similar to the OP's code. However, I got it to work with the line client.ClientCredentialApplicator = ClientCredentialApplicator.PostParameter(this.clientSecret); in the Authenticate-method. This is essential to get a working code -- I think this line forces the clientSecret to be sent as a POST-parameter to the server (instead of as a HTTP Basic Auth-parameter).
This solution assumes that you've already got a client ID, a client secret and a refresh-token. Note that you don't need to enter an access-token in the code. (A short-lived access-code is acquired "under the hood" from the Google server when sending the long-lived refresh-token with the line client.RefreshAuthorization(state);. This access-token is stored as part of the auth-variable, from where it is used to authorize the API-calls "under the hood".)
A code example that works for me with Google API v3 for accessing my Google Calendar:
class SomeClass
{
private string clientID = "XXXXXXXXX.apps.googleusercontent.com";
private string clientSecret = "MY_CLIENT_SECRET";
private string refreshToken = "MY_REFRESH_TOKEN";
private string primaryCal = "MY_GMAIL_ADDRESS";
private void button2_Click_1(object sender, EventArgs e)
{
try
{
NativeApplicationClient client = new NativeApplicationClient(GoogleAuthenticationServer.Description, this.clientID, this.clientSecret);
OAuth2Authenticator<NativeApplicationClient> auth = new OAuth2Authenticator<NativeApplicationClient>(client, Authenticate);
// Authenticated and ready for API calls...
// EITHER Calendar API calls (tested):
CalendarService cal = new CalendarService(auth);
EventsResource.ListRequest listrequest = cal.Events.List(this.primaryCal);
Google.Apis.Calendar.v3.Data.Events events = listrequest.Fetch();
// iterate the events and show them here.
// OR Plus API calls (not tested) - copied from OP's code:
var plus = new PlusService(auth);
plus.Key = "BLAH"; // don't know what this line does.
var me = plus.People.Get("me").Fetch();
Console.WriteLine(me.DisplayName);
// OR some other API calls...
}
catch (Exception ex)
{
Console.WriteLine("Error while communicating with Google servers. Try again(?). The error was:\r\n" + ex.Message + "\r\n\r\nInner exception:\r\n" + ex.InnerException.Message);
}
}
private IAuthorizationState Authenticate(NativeApplicationClient client)
{
IAuthorizationState state = new AuthorizationState(new string[] { }) { RefreshToken = this.refreshToken };
// IMPORTANT - does not work without:
client.ClientCredentialApplicator = ClientCredentialApplicator.PostParameter(this.clientSecret);
client.RefreshAuthorization(state);
return state;
}
}
The OAuth 2.0 spec is not yet finished, and there is a smattering of spec implementations out there across the various clients and services that cause these errors to appear. Mostly likely you're doing everything right, but the DotNetOpenAuth version you're using implements a different draft of OAuth 2.0 than Google is currently implementing. Neither part is "right", since the spec isn't yet finalized, but it makes compatibility something of a nightmare.
You can check that the DotNetOpenAuth version you're using is the latest (in case that helps, which it might), but ultimately you may need to either sit tight until the specs are finalized and everyone implements them correctly, or read the Google docs yourself (which presumably describe their version of OAuth 2.0) and implement one that specifically targets their draft version.
I would recommend looking at the "SampleHelper" project in the Samples solution of the Google .NET Client API:
Samples/SampleHelper/AuthorizationMgr.cs
This file shows both how to use Windows Protected Data to store a Refresh token, and it also shows how to use a Local Loopback Server and different techniques to capture the Access code instead of having the user enter it manually.
One of the samples in the library which use this method of authorization can be found below:
Samples/Tasks.CreateTasks/Program.cs

Accessing GData Calender from Google Apps account?

I'm building a simple app too that needs to access a calendar that's in my Google Apps account. But I'm having problems with authentication. I've tried the following code but it doesn't work:
Service service = new Service("<appname>");
service.setUserCredentials("<email>", "<password>");
CalendarEntry entry = (CalendarEntry)service.Get("<eventUrl>");
How do you get this to work with Google Apps? Is there any other type of authentication that I have to use for Google apps?
Update:
Unlocking the captcha solved my problem with getting the feed. Now I've hit the next wall: updating an event.
entry.Title.Text = "Foo";
entry.Update();
Gives me the GDataRequestException exception: "Can not update a read-only entry".
Im using the private calendar xml address that I got under kalendarsettings:
https://www.google.com/calendar/feeds/_%40group.calendar.google.com/private-/basic
I would recommend using Fiddler to see what http response you are getting back from Google. When I ran your code against my google apps account, I was getting back an "Error=CaptchaRequired" response. This required that I go to https://www.google.com/a/yourgoogleappdomain.com/UnlockCaptcha (replacing with your domain obviously). After I did that I was able to properly connect. You may be getting a different error code too so check for that and post it here. You could have an invalid password or invalid url or this functionality is disabled by your google apps administrator. Here is my sample code:
var calendarService = new CalendarService("company-app-version");
calendarService.setUserCredentials("<email>", "<password>");
var eventQuery = new EventQuery("http://www.google.com/calendar/feeds/user%40domain.com/private/full");
var eventFeed = calendarService.Query(eventQuery);
foreach (var atomEntry in eventFeed.Entries)
{
Console.WriteLine(atomEntry.Title.Text);
}
Make sure to replace the email, password, and email inside of the URL (url encode the # sign too).
using Google.GData.Client;
public bool ValidateGoogleAccount(string login, string password)
{
try
{
Service bloggerService = new Service("blogger", "App-Name");
bloggerService.Credentials = new GDataCredentials(login, password);
string token = bloggerService.QueryAuthenticationToken();
if (token != null)
return true;
else
return false;
}
catch (Google.GData.Client.InvalidCredentialsException)
{
return false;
}
}
Yet another solution Austin from google provides (it worked for me):
http://groups.google.com/group/google-calendar-help-dataapi/browse_thread/thread/400104713435a4b4?pli=1

Categories