Google authentication using azure services - c#

I authenticate Google account using azure services in visual studio (c#).
I got access token from Google account.
How can I fetch user information from that access token?
my server side code
var user = User as ServiceUser;
var identities = await user.GetIdentitiesAsync();
var googleCredential = identities[0] as Microsoft.WindowsAzure.Mobile.Service.Security.GoogleCredentials;
var accessToken = googleCredential.AccessToken;

You have to talk directly to the Google API with the customer token to get information about that user.
Here is a link to a blog post: http://blogs.msdn.com/b/carlosfigueira/archive/2012/10/25/getting-user-information-on-azure-mobile-services.aspx
Here is a link to the Google API: https://developers.google.com/api-client-library/dotnet/guide/aaa_oauth#windows-phone-81
Here is an example with a Node.js Backend:
function insert(item, user, request) {
item.UserName = "<unknown>"; // default
user.getIdentities({
success: function (identities) {
var req = require('request');
if (identities.google) {
var googleAccessToken = identities.google.accessToken;
var url = 'https://www.googleapis.com/oauth2/v3/userinfo?access_token=' + googleAccessToken;
req(url, function (err, resp, body) {
if (err || resp.statusCode !== 200) {
console.error('Error sending data to Google API: ', err);
request.respond(statusCodes.INTERNAL_SERVER_ERROR, body);
} else {
try {
var userData = JSON.parse(body);
item.UserName = userData.name;
request.execute();
} catch (ex) {
console.error('Error parsing response from Google API: ', ex);
request.respond(statusCodes.INTERNAL_SERVER_ERROR, ex);
}
}
});
} else {
// Insert with default user name
request.execute();
}
}
});
}

Related

Get Azure AD User Group details for particular User

We are working to integrate ASP.NET MVC web application with Azure PaaS. We are trying to get the User AD group from Azure Active directory but we have received the "Authorization Required" error.
DLL used: Microsoft.Azure.ActiveDirectory.GraphClient
Code used:
public async Task GetGroups(string objectId) {
IList groupMembership = new List();
try
{
if (objectId != null)
{
ActiveDirectoryClient client = AuthenticationHelper.GetActiveDirectoryClient();
IUser user = await client.Users.GetByObjectId(objectId).ExecuteAsync();
var userFetcher = (IUserFetcher)user;
IPagedCollection<IDirectoryObject> pagedCollection = await userFetcher.MemberOf.ExecuteAsync();
List<string> groupsname = new List<string>();
do
{
List<IDirectoryObject> directoryObjects = pagedCollection.CurrentPage.ToList();
foreach (IDirectoryObject directoryObject in directoryObjects)
{
if (directoryObject is Group)
{
var group = directoryObject as Group;
groupMembership.Add(group);
groupsname.Add(group.DisplayName);
}
}
pagedCollection = await pagedCollection.GetNextPageAsync();
} while (pagedCollection != null);
string groupscsv = string.Join(",", groupsname.ToArray());
System.Web.HttpContext.Current.Session["Groups"] = groupscsv;
Response.Redirect("~/Index.aspx");
}
}
catch (Exception e)
{
if (Request.QueryString["reauth"] == "True")
{
//
// Send an OpenID Connect sign-in request to get a new set of tokens.
// If the user still has a valid session with Azure AD, they will not be prompted for their credentials.
// The OpenID Connect middleware will return to this controller after the sign-in response has been handled.
//
HttpContext.GetOwinContext()
.Authentication.Challenge(OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
//
// The user needs to re-authorize. Show them a message to that effect.
//
ViewBag.ErrorMessage = "AuthorizationRequired";
return View();
}
Kindly help us to fix this issue or provide any other sample source to get the user AD groups from Azure Active Directory.

How to update token using refresh token in MVC Client Application?

There are two type of application in my solution
1)Web api application
2)MMC c# application
Here I created web api application which has facility of token authentication.
In this Application username and password validating from SQL server database.
i.e If any user request for web api token that user detail must be present in database table.(In user Table Id,Username,Password column are there with data).
So my web api application connected to database server.
Now I created MVC c# application which consume web api and access the data.
what I do here that when user put credential to mvc app login screen and that credential goes to api and validate them.
Api will give response of data If user credential are correct.
Here I got JSON response from web api and data like "access_token","Expire_time","refresh_token" etc
I stored all these detail in Session object.
So whenever I request for Getdata() from mvc app I passing 'access_token' to api and retuned result data.
I set web api token timeout 2 minutes.(token get deleted after 2 minutes)
So problem goes here that how I can maintain user login session in web api using refresh_token.I do not want to user again get login screen and come back to that screen.
Because every 2 minutes he will get login screen which is not correct solution.
I want some function when api get timeout access_token and mvc application again call refresh_token and continue data transaction.
Whenever your accesstoken is expired you can pass refresh token and can update the access token like this. Hope this will help you.
[AllowAnonymous]
[HttpPost]
public IHttpActionResult GetAccessToken(RefreshTokenModel getRefreshToken)
{
ApiResponse apiResponse = new ApiResponse();
apiResponse.Message = "Your session has expired. Kindly login again.";
try
{
var getHashToken = GenerateHash.GetHash(getRefreshToken.RefreshToken);
var getRefreshTokenDetails = tokenDetailBl.GetRefreshTokenDetail(getHashToken);
if (getRefreshTokenDetails != null && getRefreshTokenDetails.ExpiresUtc > DateTime.UtcNow && !string.IsNullOrEmpty(getRefreshTokenDetails.ProtectedTicket))
{
if (getRefreshTokenDetails.DeviceType == getRefreshToken.DeviceType)
{
var currentTime = DateTime.UtcNow;
var refreshTokenLifeTime = Convert.ToDouble(ConfigurationManager.AppSettings["RefreshTokenExpireTime"]);
var tokenExpiration = Convert.ToDouble(ConfigurationManager.AppSettings["AccessTokenExpireTime"]);
ApiIdentityManager apiIdentityManager = new ApiIdentityManager();
var tokenData = JsonConvert.SerializeObject(new { Ticket = getRefreshTokenDetails.ProtectedTicket, DeviceId = getRefreshTokenDetails.DeviceId });
var getIdentityToken = apiIdentityManager.GetRefreshToken(tokenData);
// Delete Old Tokens
tokenDetailBl.DeleteAccessTokenByDevice(getRefreshTokenDetails.DeviceId);
var refreshToken = new RefreshToken()
{
RefreshTokenId = GenerateHash.GetHash(getIdentityToken.RefreshToken),
DeviceId = getRefreshTokenDetails.DeviceId,
DeviceType = getRefreshToken.DeviceType,
UserId = getRefreshTokenDetails.UserId,
IssuedUtc = currentTime,
ExpiresUtc = currentTime.AddMinutes(Convert.ToDouble(refreshTokenLifeTime)),
ProtectedTicket = getIdentityToken.Ticket
};
//Save new tokens
tokenDetailBl.SaveAccessToken(new TokenDetail
{
AccessToken = getIdentityToken.AccessToken,
CreatedOn = DateTime.UtcNow,
UserId = getRefreshTokenDetails.UserId,
DeviceId = getRefreshTokenDetails.DeviceId,
DeviceType = getRefreshToken.DeviceType
});
tokenDetailBl.SaveRefreshToken(refreshToken);
//Get token cache.
CachedData cachedData = new CachedData(tokenDetailBl);
var getAllToken = cachedData.GetAccessTokens();
cachedData.UpdateTokenCache(getIdentityToken.AccessToken, getRefreshTokenDetails.UserId + ":" + DateTime.UtcNow.AddMinutes(tokenExpiration).ToFormateDateTimeString());
var getUserDetails = userBl.GetUserDetails(getRefreshToken.UserId);
getUserDetails.DeviceId = getRefreshTokenDetails.DeviceId;
getUserDetails.DeviceType = getRefreshTokenDetails.DeviceType;
getUserDetails.AccessToken = getIdentityToken.AccessToken;
getUserDetails.TokenType = "bearer";
getUserDetails.ExpiresIn = getIdentityToken.ExpiresIn;
getUserDetails.Issued = getIdentityToken.Issued;
getUserDetails.Expires = DateTime.UtcNow.Add(TimeSpan.FromMinutes(tokenExpiration)).ToString("R");
getUserDetails.RefreshToken = getIdentityToken.RefreshToken;
//Dictionary<string, string> tokenResponse = new Dictionary<string, string>();
//tokenResponse.Add("access_token", getIdentityToken.AccessToken);
//tokenResponse.Add("token_type", "bearer");
//tokenResponse.Add("expires_in", getIdentityToken.ExpiresIn);
//tokenResponse.Add("issued", getIdentityToken.Issued);
//tokenResponse.Add("expires", DateTime.UtcNow.Add(TimeSpan.FromMinutes(tokenExpiration)).ToString("R"));
//tokenResponse.Add("refresh_token", getIdentityToken.RefreshToken);
return ResponseMessage(Request.CreateResponse(HttpStatusCode.OK, getUserDetails));
}
else
{
apiResponse.Message = "Your session has expired. Kindly login again.";
}
}
}
catch (Exception ex)
{
Logger.Error(ex);
}
return ResponseMessage(Request.CreateResponse(HttpStatusCode.Gone, apiResponse));
}
You can use MVC filters to check that your access token is expired or not something like this.
[CacheAuthorize]
[HttpPost]
public IHttpActionResult GetUserList(SearchRequest searchRequest)
and after that code to check validation of access token
public class CacheAuthorizeAttribute : AuthorizeAttribute
{
public CacheAuthorizeAttribute(params string[] roles)
: base()
{
Roles = string.Join(",", roles);
}
public override void OnAuthorization(HttpActionContext actionContext)
{
Dictionary<HttpStatusCode, string> response;
if (SkipAuthorization(actionContext))
{
return;
}
var userSessionManager = new UserCacheManager();
if (userSessionManager.ReValidateSession(out response))
{
base.OnAuthorization(actionContext);
}
else
{
ApiResponse apiResponse = new ApiResponse(response.Values.FirstOrDefault());
actionContext.Response = actionContext.ControllerContext.Request.CreateResponse(response.Keys.FirstOrDefault(), apiResponse);
}
}
/// <summary>
/// Re-validates the user session. Usually called at each authorization request.
/// If the session is not expired, extends it lifetime and returns true.
/// If the session is expired or does not exist, return false.
/// </summary>
/// <returns>true if the session is valid</returns>
public bool ReValidateSession(out Dictionary<HttpStatusCode, string> errorResponse)
{
errorResponse = new Dictionary<HttpStatusCode, string>();
string authToken = this.GetCurrentBearerAuthrorizationToken();
ITokenDetailRepository tokenDetailRepository = new TokenDetailRepository();
ITokenDetailBL tokenDetailBl = new TokenDetailBL(tokenDetailRepository);
CachedData cachedData = new CachedData(tokenDetailBl);
if (!string.IsNullOrEmpty(authToken))
{
var currentUserId = this.GetCurrentUserId();
var getUserTokens = cachedData.GetAccessTokens();
if (!getUserTokens.ContainsKey(authToken))
{
//Get Data from DB
cachedData.GetAccessToken(authToken);
getUserTokens = cachedData.GetAccessTokens();
}
return CheckAccessToken(getUserTokens, authToken, out errorResponse);
}
else
{
errorResponse.Add(HttpStatusCode.Gone, "Access token not found.");
}
return false;
}
private bool CheckAccessToken(Dictionary<string, string> accessTokenDictionary, string authToken, out Dictionary<HttpStatusCode, string> errorResponse)
{
errorResponse = new Dictionary<HttpStatusCode, string>();
var hasToken = accessTokenDictionary.ContainsKey(authToken);
if (hasToken)
{
var getTokenValue = accessTokenDictionary[authToken];
var enCulture = new CultureInfo("en-US");
DateTime tokenAddedDate;
var isCorrectDate = DateTime.TryParseExact(getTokenValue.Split(new char[] { ':' }, 2)[1], "dd-MMM-yyyy,hh:mm tt", enCulture, DateTimeStyles.None, out tokenAddedDate);
if (isCorrectDate)
{
if (tokenAddedDate >= DateTime.UtcNow)
{
return true;
}
else
{
//Check Refresh token expired or not
errorResponse.Add(HttpStatusCode.Unauthorized, "Access token expired.");
}
}
else
{
errorResponse.Add(HttpStatusCode.Gone, "Invalid access token.");
}
}
else
{
errorResponse.Add(HttpStatusCode.Gone, "Invalid access token.");
}
return false;
}

Azure AD On-Behalf-Of flow for power bi management

I successfully set up "Azure AD On-Behalf-Of flow", my web api secured actions call and ms graph api calls work as well.
No I added more grants which are related to power bi. I want to read/write workspaces/reports etc from the web api
I tried that:
string[] scopes = { "Capacity.Read.All", "Capacity.ReadWrite.All",
"Content.Create", " Dashboard.Read.All", " Dashboard.ReadWrite.All",
"Data.Alter_Any", "Dataset.Read.All", "Dataset.ReadWrite.All", "Group.Read", "Group.Read.All",
"Metadata.View_Any", "Report.Read.All", "Report.ReadWrite.All", "Tenant.Read.All",
"Workspace.Read.All", "Workspace.ReadWrite.All"};
string accessToken = await _tokenAcquisition.GetAccessTokenOnBehalfOfUser(HttpContext, scopes); // error
var tokenCredentials = new TokenCredentials(accessToken, "Bearer");
using (var client = new PowerBIClient(new Uri(_powerBiConfig.ApiUrl), tokenCredentials))
{
...
}
but GetAccessTokenOnBehalfOfUser returns
AADSTS70011: The provided request must include a 'scope' input
parameter. The provided value for the input parameter 'scope' is not
valid.
Got it myself.
The code below demonstrates how to retrieve all power bi workspaces
public async Task<string> Groups()
{
string[] scopes = { "https://analysis.windows.net/powerbi/api/Dataset.Read.All"};
try
{
string accessToken = await _tokenAcquisition.GetAccessTokenOnBehalfOfUser(HttpContext, scopes);
var tokenCredentials = new TokenCredentials(accessToken, "Bearer");
using (var client = new PowerBIClient(new Uri(_powerBiConfig.ApiUrl), tokenCredentials))
{
return JsonConvert.SerializeObject(client.Groups.GetGroups().Value, Formatting.Indented);
}
}
catch (Exception exc)
{
return string.Empty;
}
}

Is it good practice to consume Login functionality from .NET Core API by storing tokens in HttpContext.Session?

I have created a .NET Core 2.0 Restful API, which has login functionality enabled with openiddict and JWS tokens. I also have a .NET Core Web App that consumes the API. Currently I am using HttpContext.Session on the client side to store and get the tokens in order to login the users. Since I am fairly new to .NET Core I wanted to ask if this is a good way to implement login functionality and if not how should I do it?
WebAPI AuthorizationController
[HttpPost("~/connect/token"), Produces("application/json")]
public async Task<IActionResult> Exchange(OpenIdConnectRequest request)
{
Debug.Assert(request.IsTokenRequest(),
"The OpenIddict binder for ASP.NET Core MVC is not registered. " +
"Make sure services.AddOpenIddict().AddMvcBinders() is correctly called.");
if (request.IsPasswordGrantType())
{
var user = await _userManager.FindByNameAsync(request.Username);
var userRole = await _userManager.GetRolesAsync(user);
var loginRole = request.GetParameters().ElementAt(3).Value;
if (user == null || userRole.ElementAt(0) != loginRole)
{
return BadRequest(new OpenIdConnectResponse
{
Error = OpenIdConnectConstants.Errors.InvalidGrant,
ErrorDescription = "The username/password couple is invalid."
});
}
// Validate the username/password parameters and ensure the account is not locked out.
var result = await _signInManager.CheckPasswordSignInAsync(user, request.Password, lockoutOnFailure: true);
if (!result.Succeeded)
{
return BadRequest(new OpenIdConnectResponse
{
Error = OpenIdConnectConstants.Errors.InvalidGrant,
ErrorDescription = "The username/password couple is invalid."
});
}
// Create a new authentication ticket.
var ticket = await CreateTicketAsync(request, user);
return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);
}
return BadRequest(new OpenIdConnectResponse
{
Error = OpenIdConnectConstants.Errors.UnsupportedGrantType,
ErrorDescription = "The specified grant type is not supported."
});
}
WebApp AccountController
[HttpPost]
public IActionResult Login(LoginViewModel vm)
{
if (ModelState.IsValid)
{
using (HttpClient httpClient = new HttpClient())
{
Dictionary<string, string> tokenDetails = null;
var fullUrl = _appSettings.Value.Apis.GSRTCApi.Url + _appSettings.Value.Apis.GSRTCApi.LoginEndpoint;
HttpClient client = new HttpClient
{
BaseAddress = new Uri(fullUrl)
};
var login = new Dictionary<string, string>
{
{"grant_type", "password"},
{"username", vm.Email},
{"password", vm.Password},
{"role", vm.Role}
};
var response = client.PostAsync("Token", new FormUrlEncodedContent(login)).Result;
if (response.IsSuccessStatusCode)
{
tokenDetails = JsonConvert.DeserializeObject<Dictionary<string, string>>(response.Content.ReadAsStringAsync().Result);
if (tokenDetails != null && tokenDetails.Any())
{
var tokenNo = tokenDetails.ElementAt(2).Value;
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + tokenNo);
HttpContext.Session.SetString("Username", vm.Email);
HttpContext.Session.SetString("Token", tokenNo);
//return RedirectToAction("Index", "Dashboard");
return Json(Url.Action("Index", "Dashboard"));
}
}
}
}
return PartialView("../Account/_Login", vm);
}

How to use Identity user managament with Cordova and OAuth.io?

I want to make a Cordova phone app and a web application. Both the application and the app share the same database.
On the mobile app, the user actions send requests to a web service ( over https ) that writes to the database. On the mobile app, I use https://oauth.io to let the user register and log in with multiple open auth providers. Trying to make it work for facebook, for now.
I just can't figure how to use the Identity user management in that context. Most of the examples I find are in the context of a web app where the user clicks and it calls the account controller. In my case, the oauth.io lib calls facebook, returns an access token, which I pass to my service.
The cordova app passes the accessToken to this method to my server side web service.
var client = new FacebookClient(accessToken);
if (client != null)
{
dynamic fbresult = client.Get("me");
if (fbresult["id"] != null)
{
var fbid = fbresult["id"].ToString();
and where do we go from now ?
how do I insert a new user
I tried this:
var user = new ApplicationUser() { UserName = fbresult["id"] };
Backend.Controllers.AccountController ac = new Controllers.AccountController();
ac.UserManager.CreateAsync(user);
Doesn't work because the usermanagement object inside the account controller is null.
There is an overload of the AccountController constructor but I have a feeling I'm doing this whole thing the wrong way.
Let's say the server side receives a facebook access token. How do use OWIN and Identity user management system from there?
Ok.
As suggested by a friend, I replaced the controllers etc from the original web api template for the ones in the Identity Sample Project
Here is the method called by the mobile app, with a angular jsonp
[OperationContract]
[WebGet(ResponseFormat = WebMessageFormat.Json)]
public string StartSession(string accessToken)
{
if (!HttpContext.Current.Request.IsAuthenticated)
{
var client = new FacebookClient(accessToken);
if (client != null)
{
dynamic fbresult = client.Get("me");
if (fbresult["id"] != null)
{
string fbid = fbresult["id"].ToString();
ApplicationUser user = null;
using (var context = new ApplicationDbContext())
{
user = context.Users.FirstOrDefault(u => u.UserName.ToString() == fbid);
}
if (user == null)
{
CreateUserAsync(fbid);
return "user created. ";
}
else
{
HttpContext.Current.Session["user"] = "holy fuck";
return "user logged in. ";
}
}
}
return "ok";
}
else
{
return "already auth !";
}
}
here is the CreateUserAsync I made
public async System.Threading.Tasks.Task<bool> CreateUserAsync(string fbid)
{
using (var context = new ApplicationDbContext())
{
var newUser = new ApplicationUser() { UserName = fbid, Email = "xxx#gmail.com" };
var userManager = new ApplicationUserManager(new UserStore<ApplicationUser>(context));
try
{
var result = await userManager.CreateAsync(newUser, "Admin#123456");
var test = await context.SaveChangesAsync();
return result.Succeeded;
}
catch (Exception ex)
{
throw ex;
}
}
}
And then, when the mobile app calls back my web service, I can check if the session exists like so:
[OperationContract]
[WebGet(ResponseFormat = WebMessageFormat.Json)]
public async Task<string> TestLogin(int id, string callback)
{
if (HttpContext.Current.Session["user"] != null)
{
return new JavaScriptSerializer().Serialize(new word() { Name = "woot" });
}
else
return new JavaScriptSerializer().Serialize(new word() { Name = "not logged" });
}
Yea, that's right. A if and a session. Just like I was doin' 13 years ago.
Also, while doing this abomination, I stumbled upon a hangin' problem in the IdentityConfig.cs file.
Apparantly, the problem is known by Microsoft and I guess it is probably fixed in the version 3 of Owin ? But I didn't know about that version 3 at that time, so I followed Program hangs during database initialization .
For some reason, some of the method posted in his solution didn't exist for me. I ended up fixing the code the best I could:
public static void InitializeIdentityForEF(ApplicationDbContext db)
{
//ApplicationUserManager userManager = HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>();
RoleManager<IdentityRole> roleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(db));
const string name = "admin#example.com";
const string password = "Admin#123456";
const string roleName = "Admin";
IdentityRole adminRole = new IdentityRole(roleName);
//Create Role Admin if it does not exist
if (!roleManager.RoleExists(roleName))
{
roleManager.Create(adminRole);
PasswordHasher hasher = new PasswordHasher();
ApplicationUser adminUser = new ApplicationUser { UserName = name, Email = name, PasswordHash = hasher.HashPassword(password), LockoutEnabled = false };
db.Users.Add(adminUser);
IdentityUserRole userRole = new IdentityUserRole() { RoleId = adminRole.Id, UserId = adminUser.Id };
adminUser.Roles.Add(userRole);
var x = db.SaveChanges();
}
}
Also just in case someone is wondering how to call the svc service from the mobile, here is the code.
(it's a little bit messy, but the important parts are there.)
(keep in mind I am using https://oauth.io/ )
$scope.refresh = function () {
$http.jsonp("https://10.0.100.38:6443/Service1.svc/helloworld?id=1&callback=JSON_CALLBACK").success(function JSON_CALLBACK(result) {
OAuth.popup('facebook')
.done(function (oauthResult) {
oauthResult.me() // standardise lesfield firstname, first-name etc
.done(function (response) {
alert("3");
$http.jsonp("https://10.0.100.38:6443/Service1.svc/StartSession?accessToken=" +oauthResult.access_token + "&callback=JSON_CALLBACK").success(function JSON_CALLBACK(result) {
alert("done " +result); // StartSession serverside success ");
}).error(function (data, status, headers, config) {
alert("icierror2" +data + " " +status + " " +headers + " " + config);
$scope.status = status;
});
}).fail(function (error) {
alert("icierror3 " +error);
});
})
.fail(function (error) {
console.log(error);
});
alert(result.Name); // result de la svc request over https
}).error(function (data, status, headers, config) {
alert("icierror" +data + " " +status + " " + headers + " " +config);
$scope.status = status;
});
Problems
As of now, I am not creating a Login, only a user is created.
Also, the project's OWIN version is 2.0, and apparantly, a 3.0 exists.
To be honest, the more I read online the more I feel like everything I've done is a big hack around the right way to do it. I just couldn't figure it out. This is thing is incredibly huge, confusing, chaothic and broken. Yea, I've added an opinion to my answer.

Categories