How to use facebook authentication with asp.net login control? - vb.net

I have been digging on facebook authentication for a week. I came across so many things such as facebook_connect, Facebook C# SDK from CodePlex and other ways to connect with facebook which are absolute now. Finally after reading http://developers.facebook.com for many times, I did manage to have a login button and get user's information for facebook using the new and standard Graph API stuffs. There are in Javascript such as.
var uid = response.authResponse.userID;
var accessToken = response.authResponse.accessToken;
var name = response.name
var username = response.username
var gender = response.gender
On another world, I have Login Control and asp.net Form authentication managing the whole website.
Protected Sub Login1_Authenticate(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.AuthenticateEventArgs) Handles Login1.Authenticate
Dim userName As String = Login1.UserName
Dim password As String = Login1.Password
Dim result As Boolean = UserLogin(userName, password)
If (result) Then
e.Authenticated = True
Else
e.Authenticated = False
End If
End Sub
Private Function UserLogin(ByVal userName As String, ByVal password As String) As Boolean
//validate user // if valid, return true
//return false if invalid user
End
I can't remove all form authentication from the existing website. Facebook login should be value added feature to the website. Now the few bits I don't get is ...
How to authenticate the asp.net Form authentication when someone Login to the website using facebook Login.
How do I pass all the value in the javascript to aspx.vb to connect to database and store the information.
I understand I would need to create a new table in the database, probably called FacebookUsers. But I can't think a way that facebook authentication and asp.net Login control to work together. My website is in VB.net by the way.

*How do I pass all the value in the javascript to aspx.vb to connect to database and store the information*.
Check out the following articles they may help you out a bit.
http://www.codeproject.com/Tips/371917/Get-user-Facebook-details-in-ASP-NET-and-Csharp
1.-Don't create a new table just add a new field to your current users table...
2.-Name the Field FaceBookUID or FB_ID.
3.-Store the facebook user id .
4.-Set the default Value of the FACEBOOK UID to 0.
5.-Now you'll have a unique value between a regular user and a FacebookUser.

Related

IdentityServer4 - Best Practises to get User Information from access token

I recently started developing using IdentityServer4. What I want to achieve is to have a number of independent web applications that use the same authorization server, my identity server.
My problem is how to make sure, that all my independend web applications have obtained and display the up to date user information (like firstName,lastName,avatar etc) which are stored in my IdentityServer4 database
I am aware that I should implement the IProfileService interface, to make sure that user-info endpoint will return all additional user info, but I dont know where to call this api request from my web applications. I have created a function that looks like this:
var t = await HttpContext.GetTokenAsync("access_token");
if (!String.IsNullOrEmpty(t))
{
var client = new HttpClient();
var userInfoRequest = new UserInfoRequest()
{
Address = "https://localhost:5001/connect/userinfo",
Token = t
};
var response = client.GetUserInfoAsync(userInfoRequest).Result;
if (response.IsError)
throw new Exception("Invalid accessToken");
dynamic responseObject = JsonConvert.DeserializeObject(response.Raw);
string firstName = responseObject.FirstName.ToString();
HttpContext.Session.SetString("User_FirstName", firstName);
string lastName = responseObject.LastName.ToString();
HttpContext.Session.SetString("User_LastName", lastName);
HttpContext.Session.SetString("User_FullName", firstName + " " + lastName);
if (responseObject.Image != null && !String.IsNullOrEmpty(responseObject.Image.ToString()))
{
string im = responseObject.Image.ToString();
HttpContext.Session.SetString("User_Image", im);
}
}
to get user Info from web applications.
My problem is when and how to call this function, every time the user redirects logged in from identity server to my web application, and how to make sure that Sessions will keep user associated data, for as much as the user remains logged in to my web application.
You can call Token Introspection endpoint to get all user info from #identityServer4.

How to get Name of Authenticated user in ASP.NET Core application using Azure Active Directory

In a ASP.NET Core application with Azure AD Connected and Configured. I am able to get the NameIdentifier using this code:
var user = User.FindFirst(ClaimTypes.NameIdentifier).Value; ✔️
When trying to get just a name with the following line of code:
var user = User.FindFirst(ClaimTypes.Name).Value; ❌
I receive the following error:
Object reference not set to an instance of an object.
Looked up in Azure Active Directory the user does have a full first and last name. Tried many examples online, it looks like the name should be showing.
UPDATE:
Finally figured it out, at first I managed to get all the human readable text like this:
foreach (Claim item in User.Claims)
{
if (item.Type.Contains("name"))
{
var username = item.Value;
}
}
this is much better
var userName = User.FindFirst("name").Value;
According to document : https://learn.microsoft.com/en-us/azure/architecture/multitenant-identity/claims :
In ASP.NET Core, the OpenID Connect middleware converts some of the claim types when it populates the Claims collection for the user principal:
oid > http://schemas.microsoft.com/identity/claims/objectidentifier
tid > http://schemas.microsoft.com/identity/claims/tenantid
unique_name >
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name
upn > http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn
So that if the unique_name exists in ID token , the middleware will populate the value of ClaimTypes.Name . But according to document : Microsoft identity platform ID tokens:
unique_name : Provides a human readable value that identifies the subject of the token. This value isn't guaranteed to be unique within a tenant and should be used only for display purposes. Only issued in v1.0 id_tokens.
So that the claim only issued in Azure AD V1.0 id tokens ,and it isn't guaranteed to be unique within a tenant .
You can get the user's name and email by (The profile scope is required to receive the claims & test with Azure AD V2.0 app):
var userName = User.FindFirst("name").Value;
var Email = User.FindFirst("preferred_username").Value;
If you want to know the first name and last name of current user , you may acquire access token for Microsoft Graph , call user endpoint with token and check the givenName/surname values from response :
https://learn.microsoft.com/en-us/graph/api/user-get?view=graph-rest-1.0&tabs=http
I struggled with the same issue and found an example and it works very well:
Basically:
public WeatherForecastController(ILogger<WeatherForecastController> logger, ITokenAcquisition tokenAcquisition, GraphServiceClient graphServiceClient, IOptions<MicrosoftGraphOptions> graphOptions)
{
_logger = logger;
_tokenAcquisition = tokenAcquisition;
_graphServiceClient = graphServiceClient;
_graphOptions = graphOptions;
}
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
HttpContext.VerifyUserHasAnyAcceptedScope(scopeRequiredByApi);
User user = _graphServiceClient.Me.Request().GetAsync().GetAwaiter().GetResult();
_logger.LogInformation("User Id: " + user.Id.ToString());
This link will give the details on how to use it with Azure AD, .net web api, and angular
active-directory-dotnet-native-aspnetcore-v2

How to new a new access token from a refresh token using vb.net?

I don't know if you can help me understand the right way forward with this issue. I need to provide a little bit of background first.
I have a VB.Net Console Utility that uses the Google V3 Calendar API. This utility has the following process to authenticate:
Private Function DoAuthentication(ByRef rStrToken As String, ByRef rParameters As OAuth2Parameters) As Boolean
Dim credential As UserCredential
Dim Secrets = New ClientSecrets() With {
.ClientId = m_strClientID,
.ClientSecret = m_strClientSecret
}
'm_Scopes.Add(CalendarService.Scope.Calendar)
m_Scopes.Add("https://www.googleapis.com/auth/calendar https://www.google.com/m8/feeds/ https://mail.google.com/")
Try
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(Secrets, m_Scopes,
"user", CancellationToken.None,
New FileDataStore("PublicTalkSoftware.Calendar.Application")).Result()
' Create the calendar service using an initializer instance
Dim initializer As New BaseClientService.Initializer() With {
.HttpClientInitializer = credential,
.ApplicationName = "~~~~~~~~~~"
}
m_Service = New CalendarService(initializer)
rStrToken = credential.Token.AccessToken.ToString()
rParameters.AccessToken = credential.Token.AccessToken
rParameters.RefreshToken = credential.Token.RefreshToken
Catch ex As Exception
' We encountered some kind of problem, perhaps they have not yet authenticated?
Return False
End Try
Return True
End Function
This part of the application process works fine. The data store file gets created and once the user has authenticated it all seems to just work find from there on. The user will be able to update the calendar without any further authenticating on there part.
Now, I also have a part of my MFC (the main application) project that sends emails for the user. This uses the following CkMainManW library.
For the most part that works too. If the user has correctly set up their credentials it is fine. However, if they are using GMail, then I do things slightly differently. This is to avoid the need to have the "Allow third party apps" option to be ticked in the Google account.
So, for GMail users, we send emails like this:
mailman.put_SmtpUsername(strUsername);
mailman.put_OAuth2AccessToken(strGoogleToken);
As you can see, I use the OAuth2AccessToken. This actual value passed is the credential.Token.AccessToken.ToString() value stored from when the user authenticated. Now, I have since understood that this actual token only lasts for one hour. This would explain why some users have to repeatedly run my calendar authentication again to get a new access token.
Clearly, when I do the calendar authentication which uses the data store file, it does something under the hood the avoid the user being asked all the time to authenticate.
Now, I have read this tutorial about using the Chilkat Library for this. I notice now that in the sample code it has a comment:
// Now that we have the access token, it may be used to send as many emails as desired
// while it remains valid. Once the access token expires, a new access token should be
// retrieved and used.
So, with all the background, how do I resolve my issue? So I have a data store file that contains the original access token from when they authorised and a refresh token. This file was created by the VB.Net command line module.
By the sounds of it, the Chilkat routine needs an access token that is valid. So, what is the right way for me to get an updated access token from the refresh token, so that when I send emails it won't fail after an hour?
Update
I am getting myself confused. I changed my code so that it called the DoAuthentification call above to get the refresh token and access token. But I am finding that the actual data store file is not getting revised. The text file is not being revised.
I have to revoke access and then do the authentication to get the data store file revised. And it is only once it has been revised that the access token will work for sending emails.
I think I have found the solution. I saw this answer:
https://stackoverflow.com/a/33813994/2287576
Based on the answer I added this method:
Private Function RefreshAuthentication(ByRef rStrAccessToken As String, ByRef rStrRefreshToken As String) As Boolean
Dim parameters As New OAuth2Parameters
With parameters
.ClientId = m_strClientID
.ClientSecret = m_strClientSecret
.AccessToken = rStrAccessToken ' Needed?
.RefreshToken = rStrRefreshToken
.AccessType = "offline"
.TokenType = "refresh"
.Scope = "https://www.googleapis.com/auth/calendar https://www.google.com/m8/feeds/ https://mail.google.com/"
End With
Try
Google.GData.Client.OAuthUtil.RefreshAccessToken(parameters)
rStrAccessToken = parameters.AccessToken
rStrRefreshToken = parameters.RefreshToken
RefreshAuthentication = True
Catch ex As Exception
RefreshAuthentication = False
End Try
End Function
I am not sure if I need to pass in the existing access token or not before refreshing. But either way, the tokens get updated and I can proceed with sending emails.
FYI, in the end it became apparent that I did not need any bespoke Refresh at all because the system manages it for you under the hood.
Private Async Function DoAuthenticationAsync() As Task(Of Boolean)
Dim credential As UserCredential
Dim Secrets = New ClientSecrets() With {
.ClientId = m_strClientID,
.ClientSecret = m_strClientSecret
}
Try
credential = Await GoogleWebAuthorizationBroker.AuthorizeAsync(Secrets, m_Scopes,
"user", CancellationToken.None,
New FileDataStore("xxx.Calendar.Application"))
' Create the calendar service using an initializer instance
Dim initializer As New BaseClientService.Initializer() With {
.HttpClientInitializer = credential,
.ApplicationName = "yy"
}
m_Service = New CalendarService(initializer)
Catch ex As Exception
' We encountered some kind of problem, perhaps they have not yet authenticated?
' Can we isolate that as the exception?
m_logger.Error(ex, "DoAuthenticationAsync")
Return False
End Try
Return True
End Function
I have not required any bespoke Refresh of tokens for a long time now.

Unable to access claims or saml attributes via Kentor.AuthServices.Owin in MVC

I am building an .net 4.5 MVC application which connects to a Shibboleth-based SAML IdP, to provide single-sign-on functionality. To do this I am using the Kentor.AuthServices.Owin middleware.
The IdP service in question requires use of encrypted assertions, which the latest build of Kentor.AuthServices doesn't support. Instead I had to use the Raschmann-fork of it here https://github.com/Raschmann/authservices/tree/78EncryptedAssertion (v0.8.1), then tried ..Raschmann/authservices/tree/Release (v0.10.1).
(Using ..Raschmann/authservices/tree/master (v0.12.1) - or indeed any of the KentorIT Kentor.AuthServices builds - results in loginInfo being null within ExternalLoginCallback.)
Using the above allows me to login/register on the application via the IdP. However, when ExternalLoginCallback is called, the ExternalClaims or Claims objects within loginInfo.ExternalIdentity don't contain any claims.
I have captured and decrypted the SAML response from the IdP and have confirmed that it is sending information (such as firstname, lastname, DoB, etc.) back to my application once I have logged in.
How can I access the SAML data that is being returned?
ConfigureAuth within Startup.Auth.vb:
Public Sub ConfigureAuth(app As IAppBuilder)
' Configure the db context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext(AddressOf ApplicationDbContext.Create)
app.CreatePerOwinContext(Of ApplicationUserManager)(AddressOf ApplicationUserManager.Create)
app.CreatePerOwinContext(Of ApplicationSignInManager)(AddressOf ApplicationSignInManager.Create)
app.UseCookieAuthentication(New CookieAuthenticationOptions() With {
.AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
.Provider = New CookieAuthenticationProvider() With {
.OnValidateIdentity = SecurityStampValidator.OnValidateIdentity(Of ApplicationUserManager, ApplicationUser)(
validateInterval:=TimeSpan.FromMinutes(30),
regenerateIdentity:=Function(manager, user) user.GenerateUserIdentityAsync(manager))},
.LoginPath = New PathString("/Account/Login")})
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie)
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5))
app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie)
app.UseKentorAuthServicesAuthentication(New KentorAuthServicesAuthenticationOptions(True))
AntiForgeryConfig.UniqueClaimTypeIdentifier = Global.System.IdentityModel.Claims.ClaimTypes.NameIdentifier
End Sub
ExternalLoginCallback within AccountController.vb:
<AllowAnonymous>
Public Async Function ExternalLoginCallback(returnUrl As String) As Task(Of ActionResult)
Dim loginInfo = Await AuthenticationManager.GetExternalLoginInfoAsync()
If loginInfo Is Nothing Then
Return RedirectToAction("Login")
End If
Dim externalIdentity = Await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie)
' Sign in the user with this external login provider if the user already has a login
Dim result = Await SignInManager.ExternalSignInAsync(loginInfo, isPersistent:=False)
Select Case result
Case SignInStatus.Success
Dim user = Await UserManager.FindAsync(loginInfo.Login)
If user IsNot Nothing Then
'user.FirstName = loginInfo.ExternalIdentity.FindFirst(ClaimTypes.Name).Value
'user.Email = loginInfo.ExternalIdentity.FindFirst(ClaimTypes.Email).Value
Await UserManager.UpdateAsync(user)
End If
Return RedirectToLocal(returnUrl)
Case SignInStatus.LockedOut
Return View("Lockout")
Case SignInStatus.RequiresVerification
Return RedirectToAction("SendCode", New With {
.ReturnUrl = returnUrl,
.RememberMe = False
})
Case Else
' If the user does not have an account, then prompt the user to create an account
ViewBag.ReturnUrl = returnUrl
ViewBag.LoginProvider = loginInfo.Login.LoginProvider
Return View("ExternalLoginConfirmation", New ExternalLoginConfirmationViewModel() With {
.Email = loginInfo.Email
})
End Select
End Function
The owin pipeline is quite complex. To debug this I'd suggest that you insert a small breakpoint middleware immediately before the UseKentorAuthServicesAuthentication() call.
app.Use(async (context, next) =>
{
await next.Invoke();
});
Sorry for using C#, but I assume you can find the equivalent VB syntax.
Run the application and authenticate. Right before you trigger the Idp to post the response back, put a breakpoint on the closing bracket of the code snippet above. Then investigate the content of the context.Authentication.AuthenticationResponseGrant. That's the actual output form Kentor.AuthServices. Are the claims present there?
If they're not, then there's a bug in AuthServices. Please report it as an issue on the GitHub Issue tracker and I'll have a look.
If the claims are indeed present at that point, but lost later, you may be a victim of the Owin Cookie Monster.

Twitterizer - how to get the username

I'm trying to get the username of an authorised user. I've already managed to get the OAuth to work, have got the token, directed the user to retrieve the PIN, entered that and sent a tweet successfully. But what I don't understand is WHEN and HOW to get the username.
I used this VB.NET code to get the URL:
Dim ReqTokens = OAuthUtility.GetRequestToken("XXX", "XXX", "oob")
Dim url = (OAuthUtility.BuildAuthorizationUri(ReqTokens.Token).AbsoluteUri)
TextBox2.Text = url
This code then confirmed authorisation and allows me to tweet using the app:
OAuthUtility.GetAccessToken("XXX", "XXX", "XXX", "1234567")
But at which stage, and HOW, do I retrieve the username and user ID?
Any help in VB.NET, and simplified for the dunce I clearly am, would be much appreciated!
OAuthUtility.GetAccesToken() returns a OAuthTokenResponse which contains the ScreenName and Id property. Doc is here
Dim accessTokens = OAuthUtility.GetAccessToken("")
Dim screenName as String = accessTokens.ScreenName