Yahoo OAuth returning error using WebAuthenticationBroker - authentication

I was trying to login using yahoo from my UWP app.
StartUri is https://api.login.yahoo.com/oauth2/request_auth?response_type=code&scope=openid&client_id=dj0yJmk9TDNtd2MxeGNMT1pUJmQ9WVdrOVQwVlNVbFpQTkdjbWNHbzlNQS0tJnM9Y29uc3VtZXJzZWNyZXQmeD05Mw&redirect_uri=http://localhost:8080
EndUri is http://localhost:8080/
WebAuthenticationResult WebAuthenticationResult = await WebAuthenticationBroker.AuthenticateAsync(
WebAuthenticationOptions.None,
StartUri,
EndUri);
It is properly showing the sign in
but after sign in it shows error page
if we press close it will direct me to yahoo home page instead of asking user consent. anyone having idea why this happen?

There are two problems in your authorization URL.
Firstly, the client_id in your URL is not right. Usually, client_id is end up with --, using the client_id in Authorization Code Flow for Server-side App for example, it is
dj0yJmk9ak5IZ2x5WmNsaHp6JmQ9WVdrOVNqQkJUMnRYTjJrbWNHbzlNQS0tJnM9Y29uc3VtZXJzZWNyZXQmeD1hYQ--
So I think your client_id in wrong.
The second problem is your redirect_uri, the redirect_uri should match the Callback Domain you've set in your app.
Please specify the domain to which your application will be returning after successfully authenticating. Yahoo OAuth flow will redirect users to a URL only on this domain (or its sub-domain) after they authorize access to their private data.
So redirect_uri need to be a domain and http://localhost:8080 don't meet this requirement. In my test I just used localhost.com for example:
public async Task<string> AuthorizeWithYahoo()
{
var clientId = "<My client id>";
var StartUri = new Uri($"https://api.login.yahoo.com/oauth2/request_auth?client_id={clientId}&response_type=code&redirect_uri=http://localhost.com");
var EndUri = new Uri("http://localhost.com");
WebAuthenticationResult WebAuthenticationResult = await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.None,
StartUri, EndUri);
if (WebAuthenticationResult.ResponseStatus == WebAuthenticationStatus.Success)
{
var responseData = WebAuthenticationResult.ResponseData;
return responseData;
}
else if (WebAuthenticationResult.ResponseStatus == WebAuthenticationStatus.ErrorHttp)
{
return $"HTTP Error returned by AuthenticateAsync() : {WebAuthenticationResult.ResponseErrorDetail.ToString()}";
}
else
{
return $"Error returned by AuthenticateAsync() : {WebAuthenticationResult.ResponseStatus.ToString()}";
}
}
And after sign in, you will see something like:

The second problem is your redirect_uri, the redirect_uri should match the Callback Domain you've set in your app.
The Redirect URL, can set in my localhost to developers on Visual Studio??

Related

How to provide own login page if windows authentication get failed?

Currently i am working on one POC with Identity server4 where i have to show my own login page if windows authentication get failed(in this case i just want to show my own login page and avoid browser login popup .
My question is where to inject my own login page in code? and how application will know windows authentication get failed?If you check below code, first request to AuthenticateAsync always return null and then it call Challenge from else block which ask browser to send Kerberos token
and we achieve SSO but now i want to show my own login page if SSO fail.
My scenario is exactly similar like this
Anyone know how to achieve this?
private async Task<IActionResult> ProcessWindowsLoginAsync(string returnUrl)
{
// see if windows auth has already been requested and succeeded.
var result = await HttpContext.AuthenticateAsync(_windowsAuthConfig.WindowsAuthenticationProviderName);
if (result?.Principal is WindowsPrincipal wp)
{
var props = new AuthenticationProperties
{
RedirectUri = Url.Action("Callback"),
Items =
{
{ "returnUrl", returnUrl},
{ "scheme", _windowsAuthConfig.WindowsAuthenticationProviderName}
}
};
var id = new ClaimsIdentity(_windowsAuthConfig.WindowsAuthenticationProviderName);
var claims = await _userStore.GetClaimsForWindowsLoginAsync(wp);
id.AddClaims(claims);
_logger.LogDebug("Signing in user with windows authentication.");
await HttpContext.SignInAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme,new ClaimsPrincipal(id),props);
return Redirect(props.RedirectUri);
}
else
{
_logger.LogDebug("Re-triggered windows authentication using ChallengeResult.");
// Trigger windows auth
// since windows auth don't support the redirect uri,
// this URL is re-triggered when we call challenge
return Challenge(_windowsAuthConfig.WindowsAuthenticationSchemes);
}
}

Clean way to create user and issue jwt token?

Currently i have an AuthenticationController which listens on post registration.
This registration method, stores the user and create a jwt token.
[HttpPost("registration")]
public async Task<IActionResult> Registration([FromBody] RegistrationViewModel model)
{
if (!ModelState.IsValid)
return BadRequest(new { code = "ModelNotValid", description = "Model is not valid." });
if (_userService.UserNameTaken(model.UserName))
return BadRequest(new { code = "UserNameTaken", description = $"The User Name
{model.UserName} is taken." });
var identityResult = await _userService.CreateUserAsync(model);
if (!identityResult.Succeeded)
return BadRequest(identityResult.Errors);
var user = _userService.GetUserByUserName(model.UserName);
int validityDurationInHours = 3;
string token = _jwtService.GenerateJwtToken(user, await _userService.GetUserRolesAsync(user),
validityDurationInHours);
return Ok(new { code = "RegistrationSuccess", auth_token = token });
}
But now i would like to refactor the code, so there would be a UsersController which saves the user and i am not sure if i should generate the jwt token in the same method as it is done now.
So i what i see as a way is when the user registers on client side it sends the data to the backend, if it was correct than the client will be notifed, which than redirects user to the login page. But i feel unnecesarry the redirection, the user data was correct so it should get the jwt token straigth away.
But i am unsure that the issueing the token should be done by the userscontroller default post method, which saves the user.
The other way i see is when the user is saved the backend notifies the client and than the client create a new request to the backend to issue the token. But then the request feels unnecessary.

Redirect URI with Client Credential Flow

I am looking into using MSAL and client credential flow, however, there is one thing I don't fully understand.
In the example provided by Microsoft:
https://github.com/Azure-Samples/active-directory-dotnetcore-daemon-v2/blob/master/daemon-console/Program.cs
The following code is used to get an access token:
var clientCredentials = new ClientCredential(_clientSecret);
var app = new ConfidentialClientApplication(_clientId, _authority, "https://daemon", clientCredentials, null, new TokenCache());
string[] scopes = new string[] { "https://graph.microsoft.com/.default" };
AuthenticationResult result = await app.AcquireTokenForClientAsync(scopes);
Whats with the redirectUri in this case?
I have tried different values as the redirectUri and it seems to work either way... but if I add a relative path or null it fails to obtain a token. What is this value supposed to be?
For a console application it makes little sense to listen on an URL, however, the documentation for ConfidentialClientApplication says that it is required.
To request access token with client credential flow , app will send HTTP POST token request to Azure AD's token endpoint with app's credential , AAD will return access token in response , redirect url is not need in this scenario . According to source code , the redirect url is not used also:
private async Task<AuthenticationResult> AcquireTokenForClientCommonAsync(IEnumerable<string> scopes, bool forceRefresh, ApiEvent.ApiIds apiId, bool sendCertificate)
{
Authority authority = Instance.Authority.CreateAuthority(ServiceBundle, Authority, ValidateAuthority);
AuthenticationRequestParameters parameters = CreateRequestParameters(authority, scopes, null,
AppTokenCache);
parameters.IsClientCredentialRequest = true;
parameters.SendCertificate = sendCertificate;
var handler = new ClientCredentialRequest(
ServiceBundle,
parameters,
apiId,
forceRefresh);
return await handler.RunAsync(CancellationToken.None).ConfigureAwait(false);
}
But you should provide a valid url when initializing the ConfidentialClientApplication at this point .

AspNet Core External Authentication with Both Google and Facebook

I am trying to implement the Form-Authentication in ASP.Net Core with Both Google and Facebook Authentications. I followed some tutorials and after some struggles, I managed to make it work both.
However, the problem is that I cannot use both authentications for the same email.
For example, my email is 'ttcg#gmail.com'.
I used Facebook authentication to log in first... Registered my email and it worked successfully and put my record into 'dbo.ASPNetUsers' table.
Then I logged out, clicked on Google Authentication to log in. It authenticated successfully, but when I tried to register it keeps saying that my email is already taken.
I tried to do the same thing for other online websites (Eg, Stackoverflow). I used the same email for both Google and Facebook and the website knows, I am the same person and both my login / claims are linked even though they come from different places (Google & Facebook).
I would like to have that feature in my website and could you please let me know how could I achieve that.
In theory, it should put another line in 'dbo.AspNetUserLogins' and should link the same UserId with multiple logins.
Do I need to implement my own SignInManager.SignInAsync method to achieve that feature? Or am I missing any configuration?
You need to link your Facebook external login to your Google external login with your email by using UserManager.AddLoginAsync, you cannot register twice using the same adresse if you use the adresse as login.
Check out the Identity sample on Identity github repo.
https://github.com/aspnet/Identity/blob/dev/samples/IdentitySample.Mvc/Controllers/ManageController.cs
To link external login to a user, the Manae controller expose methods LinkLogin and LinkLoginCallback
LinkLogin requests a redirect to the external login provider to link a login for the current user
LinkLoginCallback processes the provider response
//
// POST: /Manage/LinkLogin
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult LinkLogin(string provider)
{
// Request a redirect to the external login provider to link a login for the current user
var redirectUrl = Url.Action("LinkLoginCallback", "Manage");
var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl, _userManager.GetUserId(User));
return Challenge(properties, provider);
}
//
// GET: /Manage/LinkLoginCallback
[HttpGet]
public async Task<ActionResult> LinkLoginCallback()
{
var user = await GetCurrentUserAsync();
if (user == null)
{
return View("Error");
}
var info = await _signInManager.GetExternalLoginInfoAsync(await _userManager.GetUserIdAsync(user));
if (info == null)
{
return RedirectToAction(nameof(ManageLogins), new { Message = ManageMessageId.Error });
}
var result = await _userManager.AddLoginAsync(user, info);
var message = result.Succeeded ? ManageMessageId.AddLoginSuccess : ManageMessageId.Error;
return RedirectToAction(nameof(ManageLogins), new { Message = message });
}

How do a website post to user's twitter status ?

I have a c# mvc 4 web site,I've created a twitter app on https://dev.twitter.com/apps.
from there I want to have a button on homepage to redirect the user to my app on twitter to confirm access information. after that the web site will do a post to the user twitter saying .. "I've joined the new web site .. "
I'm managed doing the part to redirect the user to allow access information :
public ActionResult Login()
{
try
{
string url = "";
string xml = "";
oAuthTwitter oAuth = new oAuthTwitter();
if (Request["oauth_token"] == null)
{
//Redirect the user to Twitter for authorization.
//Using oauth_callback for local testing.
Response.Redirect(oAuth.AuthorizationLinkGet());
}
Now I need to make a post on the user status
How do I do that ? is there a c# wrapper for Twitter API 1.1 ?
It's a multi-step process. First you direct the user to Twitter to authorize the app, and in this redirect you supply Twitter with a call-back URL in your website. Twitter will then direct the user back to that URL with (or without if they refuse access) a code that you would use to post to Twitter on the user's behalf.
You can simplify a lot of this by using something like TweetSharp, and the code might look something like this:
// This is when the user clicks on a link on your site to use your Twitter app
public ActionResult Twitter()
{
// Here you provide TweetSharp with your AppID and AppSecret:
var service = new TwitterService(AppID, AppSecret);
// Provide TweetSharp with your site's callback URL:
var token = service.GetRequestToken("http://www.yoursite.com/Home/TwitterCallback");
// Get the fully-formatted URL to direct the user to, which includes your callback
var uri = service.GetAuthorizationUri(token);
return Redirect(uri.ToString());
}
// When twitter redirects the user here, it will contains oauth tokens if the app was authorized
public ActionResult TwitterCallback(string oauth_token, string oauth_verifier)
{
var service = new TwitterService(AppID, AppSecret);
// Using the values Twitter sent back, get an access token from Twitter
var accessToken = service.GetAccessToken(new OAuthRequestToken { Token = oauth_token }, oauth_verifier);
// Use that access token and send a tweet on the user's behalf
service.AuthenticateWith(accessToken.Token, accessToken.TokenSecret);
var result = service.SendTweet(new SendTweetOptions { Status = "I've joined the new web site .. " });
// Maybe check the "result" for success or failure?
// The interaction is done, send the user back to your app or show them a page?
return RedirectToAction("Index", "Home");
}