Adding Roles to MobileServices in Azure (ACS) - c#

I am trying to learn about how to do Roles in ACS. I have a Windows Store App (not a website :-), and I need Administrator and a Member roles - but finding even very basic documentation or tutorials for something like this is proving very difficult for me.
All I have found are a bunch of references to ASP.NET or Azure Websites stuff - which I can't use, since a Store app is not a Website - and doesn't use ASP.NET.
I have Azure Mobile Services for my Windows Store app. Is there any information that you know of that could be of help?
I'm surprised that Roles don't seem to be covered in the samples/reference/tutorials section anywhere on the Azure website (unless I've missed it - several times).
Take, for example, this scenario. Here, in the Script tab of the Management Portal, I insert some text into the table associated with the currently logged in user (via the insert function):
var SendGrid = require('sendgrid').SendGrid;
function insert(item, user, request) {
item.userId = user.userId;
request.execute(
{
success: function() {
request.respond();
// Send email in the background.
sendEmail(item);
}
}
);
}
The above code inserts some text into the table and then sends an email to that user. But, how could I adapt this code, so that it would (pseudo code):
if item.text == "administrator"
then insert userid into admin column
else
then insert userid into member column
Any help at all will be highly appreciated.

It is not really an answer, but it is also too long for a comment, and I think it is really important.
First of all, when using authentication from the Windows Azure Mobile Services, you are not using Windows Azure Access Control Service. So, there is no Active Directory, there are no (additional) claims when one uses Mobile Services. This must be very clear, because I see continuous misunderstanding and confusion. #Joey still asks for, and refers ACS as federation provider for his application, which is not the case if he relies on the Mobile Services authentication mechanism.
So, the roles assignment must totally be handled by your application logic with additional table as Jim describes.
However roles assignment in general is interesting topic, and really is not part of any tutorial or documentation. And it is not part of such, because Role Assignment is part of business logic of the application itself, not part of the Authentication service. There is very similar question here, which however again does not answer in detail how to assign roles. The foundation and main issue to solve is: How to assign administrator role. Everything else, can be solved by adding appropriate UI for the application administrators. Still, this question is not subject to a short SO answer.

Related

Adding new Roles into .NET Core Web API Authorisation without Code Change

I am trying to implement a role based authorization in my web API's and Azure Functions. So there are certain roles and corresponding AD groups looking something like this
Role AD Group
-----------------------------------
myapp.data.ro GBL ROL IT myapp data RO
myapp.data.rw GBL ROL IT myapp data write
myapp.logs.read GBL ROL IT myapp logs read
myapp.file.read GBL ROL IT myapp file read
myapp.contributor.all GBL ROL IT myapp admin
------------------------------------
So I have implemented authorization using Authorize attribute at various controller level.
But the challenge is when the requirement changed and I need to create teams level roles.
So it would be like
myapp.team1.data.ro or myapp.team2.data.ro or myapp.team2.file.read like that
So how can I handle this situation and when a new team comes how can I onboard that teams into the authorization without any code change?
Ultimately my requirement is to filter the data for different teams and at different levels like file,data,logs etc.
I have got a suggestion to use some middleware to manage the roles. But again that requires code change when new roles added, isnt it?
I thought about to have ROLES in DB and the middleware checks the request roles against that ROLES MAPPING and respond with success or forbidden (403)
Please share a good approach than this. I am a very beginner with these AD groups and roles thing in Azure.
Or is there is any service in Azure itself for the dynamic Role management and authorization
I think first you have to be sure about what is storing the role information in your deployment, and what is going to handle modifying this information.
It seems like you're using Active Directory for STORING this information.
Now, what is handling modifications? Like, who/what initially creates those Active Directory groups you already have? And what kind of event would trigger changes (how do you/someone else knows to add a new group to AD, or rather how do you/someone else knows there's a new team?).
If these groups are created for you - there's nothing else needed. When a new group is added to AD the application is going to work just fine with it (with certain caveats, like propagation time and limited OU scopes for the service account/credntials used by your service itself).
If you have to create these groups yourself... there are many ways, you need to decide how and what works best for you/your stack. Microsoft documentation has details on creating groups in Azure Active Directory (I assume that's what you're using, for self-hosted/on-prem Active Directory it might be slightly different): https://learn.microsoft.com/en-us/azure/active-directory/roles/groups-create-eligible
You can call a PowerShell script, make a web request from your C# application, or trigger an Azure Function that does it. I wouldn't be surprised if there's a NuGet package with library for interacting/adding groups to Azure AD as well. There are many options.

How do I add authentication to an ASP.NET Core minimal API using Identity, but without using Azure?

I'm sure the answer to this must be obvious, as it's an obvious thing want to do, but I can't seem to find any guidance.
I have an ASP.NET Core minimal API, and want to add authentication. I already have Identity set up in a different project, and want to use the same database (ie the same users) for the API.
I saw this blog post, which looked promising until I realised that the code there checks the user name and password as plain text (using admin as both in the sample)...
if (credentials[0] == "admin" && credentials[1] == "admin")
The problem with this is that (thankfully), Identity does not store the passwords in plain text, they are hashed, so I can't do a simple comparison.
I tried hashing the incoming password, as shown in this answer, but that didn't work as the hash came out different every time I called _userManager.PasswordHasher.HashPassword.
I tried using the ASP.NET Core's SignInManager.CanSignInAsync method to check if I could sign in with the credentials, but that required me to add the following to Program...
builder.Services.AddIdentity<User, IdentityRole>(options => {
// options removed for clarity
})
.AddDefaultTokenProviders()
.AddEntityFrameworkStores<AppDbContext>();
However, as soon as I did this, any request to the API attempted to redirect to a log-in page, which is obviously not going to work when the API is being called from code.
All I could find on Microsoft's site was this article, but that assumes you are using Azure. At the moment, I'm still developing this on my local machine, and I don't know yet whether the project owners want to deploy to Azure or their own hosted server, so the code there doesn't help me.
Anyone able to explain to me how I do what seems like such an obvious and simple task? Please let me know if there is any more info I need to provide. Thanks
Have you seen Bipin Joshi's series of articles on this subject? My guess is that you are past the first few, but you might find these useful...
Implement JWT Authentication In ASP.NET Core Minimal APIs
Integrate ASP.NET Core Identity With JWT And Minimal APIs
The one change I made when using that approach was that the getToken API call just takes two string parameters for the user name and password, instead of a User object. Given that it's only two parameters, I find this makes life easier when working with disparate projects, as you don't need the class definition. Up to you though.
In order to call the API, you'll need to call getToken first, passing in the user name and password. Once you have your token, you can then set the authentication on your HtpClient as follows...
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);

C# GMAIL API Service Account Modify - unauthorized_client

I am just starting out with the c# Google.Apis.Gmail.V1 classes and using a service account to read a mailbox.
My code works fine when I use the following snippet
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(ServiceAccountEmailAddress)
{
User = "abc#test.domain.com",
Scopes = new[] { "https://www.googleapis.com/auth/gmail.readonly" }
}.FromCertificate(certificate));
With that code I can call the following successfully
if (credential.RequestAccessTokenAsync(CancellationToken.None).Result)
{
}
But I need to modify the email messages so I changed the scope from
https://www.googleapis.com/auth/gmail.readonly
to
https://www.googleapis.com/auth/gmail.modify
I now get an exception when requesting the access token
{"Error:\"unauthorized_client\", Description:\"Unauthorized client or scope in request.\", Uri:\"\""}
I have checked the service account (*.iam.gserviceaccount.com) in the Google Developers Console and I have tried all options for permissions including OWNER which should give me Full access to all resources but no joy.
I think I am just missing a simple step but I am unsure of where to look next.
TL;DR
I would read through this, but here is the short version. I know this is an older post, but hopefully it finds you!
If you have not updated/white-listed the service account's privileges/scopes in the Google Admin Console you will need to do that, make sure the domain has API access enabled, make sure the service account is setup properly, when creating the "certificate" object be aware its parameters so that it is being instantiated correctly, check the permissions on the account being impersonated and finally make sure you've made an appropriate Google Apps service account key (could have easily made an inappropriate key type.)
White-listing Google APIs in the Admin Console
This gives the Google Apps service account the abilityto use whatever scopes you provide in your Google Apps domain.
Login to the Google Apps Admin Console by using the following link.
https://admin.google.com/
The Google Apps user account must have sufficient privileges to modify domain related settings. It does not have to be the account used to create the Google Apps project in the developer console. If the account does not have privilege you will be directed to a completely different screen with no options to click on varying domain controlling web apps like "Security", "Roles", "Support", "Groups" and etc. Instead you'll dumped onto a page that shows things like "Gmail", "Drive", "Docs" and etc. that is typical user apps. The current link it drops you at is https://apps.google.com/user/hub
Click “Security.”
Click “Show more” option at the bottom of the security options list.
Click “Advanced Settings” to get the more options.
Select the “Manage API client access” link.
Now certain API scopes must be white-listed for the desired service account. In the “Client Name” text box provide the service account’s client ID. The client ID is obtained in the developer console. In the “One or more API scopes” add the desired scopes; comma delimited.
Note, if there are existing scopes they will be removed so be sure to re-add any that will be needed.
Enable Domain Wide API Access
Login to the Google Apps Admin Console by using the following link.
https://admin.google.com/
Go to the “Security” page.
Under “API reference” section
Make sure that “Enable API access” is enabled.
Creating an Appropriate Google Apps Service Account Key (Probably this)
Go to the Google Developer Console. Login as the Google Apps user that created the Google Apps project/service account. https://console.developers.google.com/
Navigate to the particular project with which you created the service account.
Click the "Service Account" button on the left of the project's page to bring up a page with all of the project's service accounts.
Click the vertical ellipse widget all the way to the right of the desired service account's row. Select “Create Key.”
Select .p12 key as it looks like this is what you're trying to use. Click "Create." Be sure to protect this key.
I have found that if the key is not created this way then it leaves open the possiblity for making either an API key or an OAuth 2.0 client/user key. These are the wrong types of keys to use in this case you would need to have created a service account key. The way outlined above forces you to create a service account key.
Modifying the Existing Google Apps Service Account's Settings
I'm not going over how to setup the actual service account, one thing you may need in your case is to make sure that the service account has domain wide delegation enabled. This is toggled in the Google Developer Console. Should be pretty easy to find.
Code
You do not provide your entire code base for creating the token, so just want to add a few things you might be doing improperly.
Make sure when you create the certificate that the secret you provide is the default "notasecret" string. This secret is currently the default value provided by all keys distributed by Google and is immutable during key creation. I had a link to prove this, but have since lost it.
X509Certificate2 certificate = new X509Certificate2(certificateFilePath, "notasecret", X509KeyStorageFlags.Exportable);
Just trying to advocate proper coding. While I have found some bugs in the past with Google's constant values that required additional string manipulation (adding additional slashes.) You should really be using the string constants that they provide in place of literals. I only say to use these because it provides a layer of abstraction, who is to say Google will never change the literal; unlikely.
In your case the new scope is:
GmailService.Scope.GmailModify
While the old scope was:
GmailService.Scope.GmailReadonly
Otherwise, everything code wise looks good to me.
Another thing to try would be to make sure that the actual Google Apps user account being impersonated by the service account has sufficient privileges. I would suspect a different error if this were the case, would be getting a 403 in the response instead. Anyway, in your case this is the "abc#test.domain.com" account. Once again you would go to the Google Admin Console, check its roles make sure it has sufficient roles checked for whatever it is you're trying to do. I don't know what specifically you'll need in this case, best bet would be to give it the same permissions as the "Super Admin" role then remove permissions as you go to see what it might actually need. Otherwise, if possible just give it "Super Admin."
If I was a gambler I would put my money on an inappropriately created service account key. I just recently ran into this and it was the only thing that produced the same exact error you're receiving. Other things would get me the same "Description" value in the response token, but not the same "Error" value. I'm not really even sure how the culprit key was made, because I didn't make it. I just know the fix was to recreate a new key with the steps above and that fixed the issue.

Web API token authentication with a custom user database

I am developing a Web API 2.1 service that needs to authenticate the connecting clients (HTML5/JS clients that I will create and control). Unfortunately, the user information (username, password hashes, roles and much, much more info) is stored in an existing (SQL Server) database to which I only have read access. The Users database table was created 5-6 years ago without any reference to security frameworks, so it's a completely custom format. I'm not allowed to make any changes to either the data or the database structure.
Inspired by this article, I rolled my own token-based method of authenticating users, but I'm lacking the completeness and (re)assurance of using an established security framework.
Is there a way to integrate an existing framework, e.g. OAuth2, within my current project given the constraints I mentioned above? I don't know if it makes any difference, but I'm self-hosting using OWIN.
This is a good answer to a similar question.
It basically says:
Make a custom user class which implements IUser
Define a custom user store which implements public class UserStoreService
: IUserStore<CustomUser>, IUserPasswordStore<CustomUser>
wire everything up
Since the answer is pretty extensive I just provided the basic steps...
details are here: How to customize authentication to my own set of tables in asp.net web api 2?
This is also a very valuable content which also applies to web api:
Customizing ASP.NET Authentication with Identity by JumpStart
https://channel9.msdn.com/Series/Customizing-ASPNET-Authentication-with-Identity
HTH
Someone else, having the competence, can explain the options. But if authentication as service is an option, then check out Auth0 # https://auth0.com
I have tested the service (as Azure plugin) using both HTML/JS- and native Windows Phone applications, against simple Sql Server table and AD. Works liek charm, near zero headache.
I stumbled upon a my solution while trying to implement json token authentication within web api. It is important to note that my solution handles authentication by sending a json token through the Authentication header of the Http request (not via cookies) and not using Microsoft.Identity framework.
Anyway, I basically implemented in a cookbook fashion the solution helpfully described here by Taiseer Joudeh: http://bitoftech.net/2014/10/27/json-web-token-asp-net-web-api-2-jwt-owin-authorization-server/
The key thing to notice is the following bit of code:
//Dummy check here, you need to do your DB checks against memebrship system http://bit.ly/SPAAuthCode
if (context.UserName != context.Password)
{
context.SetError("invalid_grant", "The user name or password is incorrect");
//return;
return Task.FromResult<object>(null);
}
Naturally you would replace this bit of code above with your own method for checking your (presumably pre-existing) user database(s). Once I implemented this I realized that you don't need to use new code first identity framework that Visual Studio installs for you.
To make this work I did the following:
1) Created an an empty project and selected Change Authentication/Individual User Accounts. This installs most of the required references and files you need out of the box to use token authentication by way of cookies as well as the code-first identity framework files.
2) Edited these files following Taiseer Joudeh's lead. This requires
some new objects such as CustomOAuthProvider.cs among others. And you need to implement your own user/password check by customizing this code block:
if (context.UserName != context.Password)
{
context.SetError("invalid_grant", "The user name or password is incorrect");
//return;
return Task.FromResult<object>(null);
}
Link to Taiseer Joudeh's instructions: http://bitoftech.net/2014/10/27/json-web-token-asp-net-web-api-2-jwt-owin-authorization-server/
3) Pruned my project of extraneous files (AccountBindingModels.cs, AccountViewModels.cs, IdentityModels.cs, ApplicationOAuthProvider.cs, identityConfig.cs, AccountBindingModels.cs, AccountViewModels.cs). Basically, No more microsoft identity references.
I am sure the microsoft.identity thing is excellent, but I was annoyed with the code-first implementation of databases when I was already using some legacy databases of a different structure etc. Hope this helps. I am quite satisfied with the result (after a few days of messing around to get it to work).
I did not want to use any existing classes and finally come out some thing very simple like
var userName = context.UserName;
var password = context.Password;
var userService = new UserService(); // our created one
var user = userService.ValidateUser(userName, password);
if (user != null){
.......
}
See the full details here OAuth Web API token base authentication with custom database
For Role base authentication with custom database
Hope it will help
This might be a completely insane and invalid approach for you but I faced a similar challenge: New web app (MVVM + WebAPI), legacy system used to issue and validate tokens. Inspired by http://tech.pro/tutorial/1216/implementing-custom-authentication-for-aspnet, and because my application would primarily be used by its accompanied GUI (the MVVM webapp), I decided to use a "cookie based" token produced by FormsAuthentication. The FormsAutnentication cookie/ticket is secured by .net internal magic security (which I assume id completely safe and unbreakable).
In my case the cookie simply holds the ticket issued by the legacy system, (but you could store more details there as well, eg by JSONSerializing a custom type). During authorization, my system validates the token against the legacy system. I guess you could use something similar together with a custom AuthorizationFilter.

MVC3 + How to get the current logged on user's user name

I am new to MVC and actually new to web development all together. I have about 7 years of development experience but in services, database, object models, etc.. basically middle-tier and back-end development. I am trying to learn ASP.NET and decided to build a site using MVC3 for a personal site for myself. I will be hosting this from an account at dotnet-hosts.com. Here is my question... I don't have a domain and I will be using the built in membership provider. I noticed in the auto generated code that was created when I added the project template that in the AccountController in the method ChangePassword (ChangePasswordModel model) there is this line of code...
MembershipUser currentUser = Membership.GetUser(User.Identity.Name, true /* userIsOnline */);
My question is specifically around User.Identity.Name, this looks like it would be returning the Windows user name just like Environment.UserName would. The Visual Studio template I used is the (Mobile Ready HTML5 MVC.NET) as I want to be able to support clients from any device...Windows PC, Apple, Windows Phone, iPhone, etc... If the call to User.Identity.Name is correct then I would like to ask how does this work on devices that are not Windows like an iPhone? If my assumption is correct that this will only work for Windows computers with a domain then how can I achieve this? would I need to perhaps use some caching? If so could I maybe grab the user name and their IP address to be used as the cache key from the Authentication page?
My high level question is... How do I get the current logged in user's userName regardless of the device/platform? I know this question is probably not written well and may be hard to understand... I apologize for that. I am new to web development and trying to get my feet wet and would like to start to the latest technology.
The call is correct. The User.Identity.Name is filled out by whatever authentication provider is in use - Windows authentication, Forms authentication, some custom authentication provider, or whatever. It isn't bound to a specific user "type". The authentication provider has the responsibility of making sure the Identity object corresponds to the current user on every request. Usually that part is taken care of using a combination of cookies and database.
The MVC template (although I haven't had a look at the template since MVC 2) uses ASP.NET's Membership class, which in turn uses a membership provider - for example SqlMembershipProvider or ActiveDirectoryMembershipProvider - the former stores your users' credentials (username and password etc.) in an SQL Server database, the latter uses Active Directory (i.e. primarily Windows logons). SqlMembershipProvider is the default, and MVC is set up to use a local SQLExpress database file as its user store.
The authentication provider that's implemented in the template project uses FormsAuthentication, which does the login procedure through a simple HTML form (the one in the LogOn view) and keeps the user signed in by way of an encrypted cookie. Works on any platform.
The setup for both FormsAuthentication and SqlMembershipProvider can be found in web.config (the one in the root of the site). There you can find the connection strings for the SQLExpress database (and e.g. change them to use a "real" SQL Server if needed), the timeout for logins etc.
(Note that you can do a lot of that configuration easily in a GUI through the "ASP.NET Configuration" button in the toolbar of Solution Explorer in Visual Studio - it also provides an easy way to set up the first users).
In short, it's all ready to go - and doesn't lock out non-Windows users.
Like you said User.Identity.Name is indeed correct. for returning the logged in users name. But the membership section like you said, provides only windows accounts. You can use similar without the user of windows accounts, to work in every scenario, and can still verify against windows if present. If you call it without membership, and follow the default MVC3 template it should work fine.
String Username = User.Identity.Name;
When you log on, using the template MVC3, it creates an authcookie. See account controller code. Here, two parameters are passed into it. The username, and to persist (when browser is closed - login is still cached).
The username is a string field, which is what is called by User.Identity.Name and infact, anything can be put into it, and is not in anyway linked to Windows login.
You could test the login via method you desire, and if yes, set a cookie using the authcookie method. (its encripted). And set the username to what ever you want. And if your verification of the user fails, dont create one, and redrect back to page.
See the example code. This is all from memory, as I dont have code infront of me for reference. But its all in the account controller, Login Action.
When the cookie is set, The users login state is cached for the session. You will need to ensure the user is logged in when visiting a webpage. Otherwise loggin in will be pointless. This is a simple attribute on the controller/action.
Note: dont do this to the Account/logon controller, as you wont be able to visit the logon page, as you are not logged in.
[Authorize]
public ActionResult DoSomething()
{
// ...
}
Hope I have helped.

Categories

Resources