MVC4/ Google OpenID limit to specific Google Apps Domain(s) - c#

I created a new MVC4/.NET4.5 project and enabled Google OpenID. This worked, shockingly easily.
My company has "gone google" and our domains/ employee identities are in the Google Apps webspace.
How can I allow only our Google Apps domains to authenticate to my new website? I'm hoping it's a simple thing like the authentication piece was.
Here is some additional information:
I literally created a default web application and enabled the Google Authentication piece. I could not believe how simple it was to validate against Google.
My company has literally hundreds of email domains, all rolled up under one email domain "umbrella". For example, my company's corporate email domain name is "foo.com", but under this we have "x.foo.com", "bar.com", and "yomommasougly.net". All of these are part of the "foo.com" Google Apps domain.
The ultimate goal is, a description of what needs to be done (and where) to take this default application and restrict it to all domains under the "foo.com" domain.
With hundreds of domains, and more being added all the time, it is not practical to specify every domain explicitly.

Assuming you're using DotNetOpenAuth check out the authentication code for the Stack Exchange Data Explorer.
Essentially, you just ask for the e-mail address with your request:
request.AddExtension(
new ClaimsRequest
{
Email = DemandLevel.Require,
}
);
Then check the returned address against your domain whitelist (I'm assuming you're already only accepting google OpenIDs)
var sreg = response.GetExtension<ClaimsResponse>();
If (!HasWhiteListedDomain(sreg.Email)) {
// Fail Here
}
Note that these bits of code need to be added to your Web.config to get the exact code for fetching the e-mail above working:
<configSections>
<section name="dotNetOpenAuth" type="DotNetOpenAuth.Configuration.DotNetOpenAuthSection" requirePermission="false" allowLocation="true" />
</configSections>
<dotNetOpenAuth>
<openid>
<relyingParty>
<behaviors>
<!-- The following OPTIONAL behavior allows RPs to use SREG only, but be compatible
with OPs that use Attribute Exchange (in various formats). -->
<add type="DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform, DotNetOpenAuth" />
</behaviors>
</relyingParty>
</openid>
</dotNetOpenAuth>
Edit:
If using OAuthWebSecurity getting the e-mail will just look something like this:
var userDataFromProvider = result.ExtraData;
var email = userDataFromProvider["email"];
Source

Related

Implement ADFS with asp.net roles

I have been working on a project where I have a simple web page integrated with AD FS. The authentication and website are working as expected. I am using VS 2015. My goal is to limit what users can access at the site, "roles" from what I have read and researched. If the logged on user is an admin, grant full access, but if logged on as a regular user limit what pages are available.
Here is the scenario, go to my project URL which is redirected to AD FS sign on, after successful sign on you are at my website. Not much to it.
I have read so much online about different ways to achieve my goal that I am unsure which course is best or simplest to configure. What are my best options here? Keep in mind I have never developed in asp or any other code for that matter. Any help would be appreciated.
There is policy based authorization that is probably the current best practice, however it sounds like role based authorization may be sufficient for you.
To perform role based authorization you'll first need to setup a claim rule in your ADFS for the Relying Party Trust of your application that sends the Role claim type "http://schemas.microsoft.com/ws/2008/06/identity/claims/role". The claim rule would look like this,
c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname", Issuer == "AD AUTHORITY"]
=> add(store = "Active Directory", types = ("http://schemas.microsoft.com/ws/2008/06/identity/claims/role"), query = ";tokenGroups;{0}", param = c.Value);
Then when your roles arrive at your application in these claims, you'll process them with Windows Identity Foundation (WIF), which is integrated into .NET Framework 4.5+. I believe referencing System.Security.Claims is sufficient to get WIF in your project for processing roles. This "processing" however is done for you by WIF.
At this point you should be able to simply decorate controllers and methods like the following to perform role based authorization, with these Roles equating to the names of groups you are a member of in Active Directory.
[Authorize(Roles = "Administrators")]
public class AdminController : Controller
{
}
Just for interest, there are other ways to do this.
Once you have the roles, you can use IsInRole(role).
You can also use the web.config e.g.
<location path="Page.aspx">
<system.web>
<authorization>
<allow roles="Admin, OtherAdmin" />
<deny users="*" />
</authorization>
</system.web>

Is this the correct way for ASP.NET web application to do authentication?

ASP.NET Web Forms applications.
I checked our company's legacy code, the way they do login is like this:
when user is validated against database with (username, password), they set a session:
Session["authenticated"] = "true";
Every page other than login.aspx is inherited from a class named SecurePage. In SecurePage's OnInit() method, it checks
if (Session["authenticated"] != null)
if true, means authenticated, otherwise means not. So basically the way to do authentication is to see if there is a session named authenticated.
This seems the most crude and intuitive way of doing authentication... I want to ask: is this safe?
Another thing I feel strange is that in the web.config, they have this:
<authentication mode="Windows" />
Shouldn't it be
<authentication mode="Forms" />
since these are web applications? Users credentials are stored in database and these users are outside clients (not internal users).
A slight different version also does this after user is validated against database:
FormsAuthentication.SetAuthCookie( username, true );
what does this do? Besides this statement in login.aspx, I don't see any other pages have any code related to auth cookie. Do we need to set auth cookie by ourselves in code or does .NET framework handle this for us already?
Still another version have the following:
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(login, false, 60);
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(ticket));
Response.Cookies.Add(cookie);
what does this do? Do we need to set this cookie by ourselves in code? or does .NET framework handle this for us already?
These web appliations were developed a long time ago, though I am not sure when. I suspect it is .NET 1.0 era? From my understanding since .NET 2.0,
it has this ASP.NET membership thing, and we can just use <authentication> and <authorization> tags in web.config (and subfolder web.config) to achieve the goal of authentication and authorization. Isn't it? Can anyone give me a history of ASP.NET framework authentiation mechanism? (membership -> simple memebership -> Identity?)
<authentication mode="Windows" />
Above should be used mostly in intranet website, because it's like saying use the computer(windows pc) authentication to access the resource. However, this will never work, since inherited class has a method to validate session key value for a login.
You should make sure that the code redirects user to login page in case the session key is not found. That means, in the else section of below code, you should take users to login page to try again. Which I am sure is happening.
if (Session["authenticated"] != null)
{ /*user is authenticated*/ }else{ /*redirect to login*/ }
Its is recommended to use <authentication mode="Forms" /> if the website is accessible over the internet. Other benefit of using this setting is that you can set default and login page.
Finally, FormsAuthenticationTicket is a class with property and values that are used when working with Forms authentication to identify authenticated users.
Read through msdn article to know more about asp.net membership.
https://msdn.microsoft.com/en-us/library/yh26yfzy%28v=vs.140%29.aspx

ASP.NET Active Directory role provider

This is my first time working with ASP.NET Role membership in active directory.
So far i've got a website running, and im able to log in with a active directory user.
My problem is: I cant get "Roles.IsUserInRole" to trigger. It's like it dosent even look at the logged in user for group memberships.
I have been searching for a solution, but the only solution i can find is to write my own membership provider. Is this really neccesary?
I want to control what the users can access with their memberships.
Like if a user is in the "students" security group in the AD then they can only access pages in a student fold in my ASP.NET solution.
I am useing form authentication.
Here is a sample of my webconfig for my rolemanager:
<system.web>
<roleManager defaultProvider="WindowsProvider"
enabled="true"
cacheRolesInCookie="false">
<providers>
<add
name="WindowsProvider"
type="System.Web.Security.WindowsTokenRoleProvider" />
</providers>
</roleManager>
</system.web>
and here im trying the IsUserInRole
protected void Login2_LoggingIn(object sender, LoginCancelEventArgs e)
{
if (Roles.IsUserInRole("Students"))
{
Response.Redirect("../Students/StartPage.aspx");
}
}
Bonus question: I am only able to login with users from the "Users" container ind my AD. Why cant i login with a user from a OU some levels down?
From your description, you appear to be using Forms Authentication with ActiveDirectoryMembershipProvider for authentication.
This is not compatible with WindowsTokenRoleProvider. To use WindowsTokenRoleProvider, which exposes roles based on Windows group membership, you need to be using Windows authentication.
As to your first question, have you already tried to use the group name together with the domain name i.e. Roles.IsUserInRole(#"DOMAIN\groupName")?
As to your second question. I assume that you use ActiveDirectoryMembershipProvider. If so, I think that you have a connection string in your web.config (which is used by the provider)and this connection string specifies that the provider should use Users container. However, you don't have to specify the concrete conatainer (for details see this site). For example instead of:
LDAP://testdomain.test.com/CN=Users,DC=testdomain,DC=test,DC=com
You can use:
LDAP://testdomain.test.com
Make sure you Remove:
<authentication mode="Forms">....</authentication>
then you can alternatively use:
User.IsInRole("Students");

Query an AD domain via SSL

I have a domain and port number (636) as well as a username and password.
I am trying to figure out a way to connect to this AD via Secure LDAP and get a users 'givenname', 'sn', 'mail', and probably a few custom attributes.
However I have no idea how to do this in C#.
I think that Microsoft may have a method for this available already but I am going to defer to you all.
The final user experience will be: See login screen, enter username and password, those credentials are sent over LDAP and the users info is returned to my web app, then I log them in if it all went well... though I don't know what a failed attempt would look like either so I can deny them. Any ideas?
Please include code samples so I can understand the implementation, thanks!
Did you even try google?
EDIT
Sorry for the hubub and the snarky response. I think the problem you were having is you didn't quite ask the question right -- either here or on google. Anyhow, you don't need a lick of C# code here. You just need to configure your web app to use AD as a membership provider. You'll need a connection string [getting this right was the hardest part]:
<connectionStrings>
<add name="MyAd"
connectionString="LDAP://adserver/OU=Users"
/>
</connectionStrings>
And a membership provider:
<membership defaultProvider="AdProvider">
<providers>
<add
name="AdProvider"
type="System.Web.Security.ActiveDirectoryMembershipProvider,
System.Web, Version=2.0.0.0,
Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
connectionStringName="MyAd"
applicationName="ItRemoteHelpdesk"
enablePasswordReset="false"
/>
</providers>
</membership>
Then users can login with their normal username#domain and password.
The System.DirectoryServices.AccountManagement is the .NET dll to use for the newer, non-LDAP AD authentication.
Try this website for a good starting point with code examples:
http://www.codeproject.com/KB/system/usingAccountManagement.aspx
You should definitely check out the .NET 3.5 System.DirectoryServices.AccountManagement namespace as suggested by Brad.
To get a good head start on how to use it, read this MSDN Magazine article: Managing Directory Security Principals in the .NET Framework 3.5
The article does talk several times about how to securely (using SSL) connect to your AD domain, and how to e.g. create users or retrieve user information. I think reading that article closely and trying out the code samples should give you a good idea on how to do what you're looking for.
Update: quite obviously, all those method in S.DS.AM require you to be authenticated against AD. The new classes also provide for pretty simple verification of user credentials (as shown in that article I linked to):
// establish context
PrincipalContext domain = new PrincipalContext(ContextType.Domain);
// determine whether a user can validate to the directory
bool validated = domain.ValidateCredentials("user1", "Password1");

How do you pass an authenticated session between app domains

Lets say that you have websites www.xyz.com and www.abc.com.
Lets say that a user goes to www.abc.com and they get authenticated through the normal ASP .NET membership provider.
Then, from that site, they get sent to (redirection, linked, whatever works) site www.xyz.com, and the intent of site www.abc.com was to pass that user to the other site as the status of isAuthenticated, so that the site www.xyz.com does not ask for the credentials of said user again.
What would be needed for this to work? I have some constraints on this though, the user databases are completely separate, it is not internal to an organization, in all regards, it is like passing from stackoverflow.com to google as authenticated, it is that separate in nature. A link to a relevant article will suffice.
Try using FormAuthentication by setting the web.config authentication section like so:
<authentication mode="Forms">
<forms name=".ASPXAUTH" requireSSL="true"
protection="All"
enableCrossAppRedirects="true" />
</authentication>
Generate a machine key. Example: Easiest way to generate MachineKey – Tips and tricks: ASP.NET, IIS ...
When posting to the other application the authentication ticket is passed as a hidden field. While reading the post from the first app, the second app will read the encrypted ticket and authenticate the user. Here's an example of the page that passes that posts the field:
.aspx:
<form id="form1" runat="server">
<div>
<p><asp:Button ID="btnTransfer" runat="server" Text="Go" PostBackUrl="http://otherapp/" /></p>
<input id="hdnStreetCred" runat="server" type="hidden" />
</div>
</form>
code-behind:
protected void Page_Load(object sender, EventArgs e)
{
FormsIdentity cIdentity = Page.User.Identity as FormsIdentity;
if (cIdentity != null)
{
this.hdnStreetCred.ID = FormsAuthentication.FormsCookieName;
this.hdnStreetCred.Value = FormsAuthentication.Encrypt(((FormsIdentity)User.Identity).Ticket);
}
}
Also see the cross app form authentication section in Chapter 5 of this book from Wrox. It recommends answers like the ones above in addition to providing a homebrew SSO solution.
If you are using the built in membership system you can do cross sub-domain authentication with forms auth by using some like this in each web.config.
<authentication mode="Forms">
<forms name=".ASPXAUTH" loginUrl="~/Login.aspx" path="/"
protection="All"
domain="datasharp.co.uk"
enableCrossAppRedirects="true" />
</authentication>
Make sure that name, path, protection and domain are the same in all web.configs. If the sites are on different machines you will also need to ensure that the machineKey and validation and encryption keys are the same.
If you store user sessions in the database, you could simply check the existance of the Guid in the session table, if it exists, then the user already authenticated on the other domain. For this to work, you would have to included the session guid in the URL when you redirect the user over to the other website.
Not sure what you'd use for .NET but ordinarily I'd use memcached in a LAMP stack.
The resolution depends on the type of application and environment in which it is running. E.g. on intranet with NT Domain you can use NTLM to pass windows credentials directly to servers in intranet perimeter without any need to duplicate sessions.
The approach how to do this is generally named single sign-on (see Wikipedia).
There are multiple approaches to this problem, which is described as "Cross-domain Single Sign On". The wikipedia article pointed to by Matej is particularly helpful if you're looking for an open source solution - however - in a windows environment I belive you're best off with one of 2 approaches:
Buy a commercial SSO product (like SiteMinder or PingIdentity)
Use MicroSoft's cross-domain SSO solution, called ADFS - Active Direcctory Federation Services. (federation is the term for coordinating the behavior of multiple domains)
I have used SiteMinder and it works well, but it's expensive. If you're in an all MicroSoft environment I think ADFS is your best bet. Start with this ADFS whitepaper.
I would user something like CAS:
[1]: http://www.ja-sig.org/products/cas/ CAS
This is a solved problem and wouldn't recommend rolling your own.
Alternatively if you want to roll your own and the sites in question are not on the same servers or don't have access to a shared database (in which case see the above responses) then you could place a web beacon on each of the sites which would refer back to the other site.
Place a single pixel image (web beacon) on site A which would call site B passing through the users ID (encrypted & time stamped). This would then create a new user session on site B for the user which would be set as logged in. Then when the user visited site B they would already be logged in.
To minimise calls you could only place the web beacon on the home page and or log in confirmation pages. I've used this successfully in the past to pass information between partner sites.

Categories