I need to navigate to a web site and set a number of claims for that web site to use, but I cannot find any explanation on how to do it. I must be googling the wrong words.
I'm using c# and .Net framework 4.6.1
Edit 1
I was asked to explain my challenge better. I am developing 2 separate websites. Users will always go to website A where they will authenticated using Azure B2C. Once authenticated they will perform a few actions after which they will be navigated to website B. Azure B2C passes user data to website A using Claims, which I would like to forward to website B
What you need to do is to expose a new login action in your website B. This login action can receive an encrypted token which inside contains the claims you are interested in forwarding to the site B. Once that encrypted token is received, the website B can decrypt it and recreate the claims that you are interested in. You can use the built in JwtSecurityToken Handler:
https://msdn.microsoft.com/en-us/library/system.identitymodel.tokens.jwtsecuritytokenhandler(v=vs.114).aspx
For example:
public string CreateSecurityToken(string audience, IEnumerable<Claim> claims)
{
if (claims == null) return null;
var credentials = new SigningCredentials(
new InMemorySymmetricSecurityKey(Encoding.UTF8.GetBytes("SOMEKEY")),
"http://www.w3.org/2001/04/xmldsig-more#hmac-sha256",
"http://www.w3.org/2001/04/xmlenc#sha256");
var claimsIdentity = new ClaimsIdentity(claims, "Custom Authentication", System.Security.Claims.ClaimTypes.Name, System.Security.Claims.ClaimTypes.Role);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = claimsIdentity,
TokenIssuerName = TokenIssuer,
AppliesToAddress = "http://" + audience,
Lifetime = new Lifetime(DateTime.UtcNow, DateTime.UtcNow.AddMinutes(GetTokenExpirationMinutes(audience))),
SigningCredentials = credentials
};
var jwtHandler = new JwtSecurityTokenHandler();
var token = jwtHandler.CreateToken(tokenDescriptor);
return jwtHandler.WriteToken(token);
}
this will create a token which you can send to a login method on website B
A claim from website A will not be sent to website B.
This could work this way:
A users logs on to site A (using credentials from B2C)
User saves info into B2C (using Graph API)
Redirect to Site B
User logs on to site B (this can occur without prompting the user, same B2C)
Claim are created for site B, but they can contain values set during step 2
graph API:
https://azure.microsoft.com/en-us/documentation/articles/active-directory-b2c-devquickstarts-graph-dotnet/
If the info is already in the B2C directory, the example is even more simple, create 2 sites:
https://azure.microsoft.com/nl-nl/documentation/articles/active-directory-b2c-devquickstarts-web-dotnet/
Create a custom attribute to store the information:
https://azure.microsoft.com/nl-nl/documentation/articles/active-directory-b2c-reference-custom-attr/
Create a B2C custom attribute:
Create 2(both) B2C Applications
Create a single B2C sign-in policy using the custom attribute and add it to the claims
Use the same policy in both applications, both should see the same claim
Related
I'm developing a .Net Core 3.1 API that will connect to Microsoft Graph to update photos of company employees.
However, I searched in several places and didn't find any code or method that could help me change a user's photo without me being logged in with that user's account.
Is there any way I can change the photo of a user with Microsoft Graph without me being logged in with the same?
Remembering that my user is the administrator of Azure AD and my application has "User.ReadWrite.All" released.
Firstly, what you need is this api, and as you can see it provides the application permission so that you can use client credential flow to generate access token and use it to call the api. Client credential flow makes users don't need to sign in and generate access token, then it can meet your requirement.
Here's the detail, you can generate token by this request, pls not you need to set User.ReadWrite.All application permission for you azure ad app:
and you can call the api with the token like this:
If you found your token can't work correctly, maybe you can try to add Group.ReadWrite.All application permission to your azure ad app too, as this is a known issue mentioned in the api document.
And maybe what you need is code snippet, then you can use graph sdk to call ms graph api:
var scopes = new[] { "https://graph.microsoft.com/.default" };
var tenantId = "your_tenant_name.onmicrosoft.com";
var clientId = "azure_ad_app_client_id";
var clientSecret = "client_secret_for_the_azuread_app";
var clientSecretCredential = new ClientSecretCredential(
tenantId, clientId, clientSecret);
var graphClient = new GraphServiceClient(clientSecretCredential, scopes);
using var stream = new System.IO.MemoryStream(Encoding.UTF8.GetBytes(#"Binary data for the image"));
await graphClient.Users["user_id_which_you_wanna_change_photo_for"].Photo.Content
.Request()
.PutAsync(stream);
I have an asp.net application where I am trying to integrate AD authentication with out redirecting the user to https://login.microsoftonline.com. I used the following code and did some modification as per the latest documentation but I am getting an error as follows
Microsoft.IdentityModel.Clients.ActiveDirectory.AdalException: 'unknown_user_type: Unknown User Type'
Here is the code I have written
var tenant = System.Configuration.ConfigurationManager.AppSettings["Tenant"];
var serviceUri = "https://login.microsoftonline.com/4061611f-98d4-4484-944c-3796d4c9746f/oauth2/authorize";
var clientID = System.Configuration.ConfigurationManager.AppSettings["ClientId"];
var userName = "";
var password = "";
var authority = string.Format(System.Globalization.CultureInfo.InvariantCulture, System.Configuration.ConfigurationManager.AppSettings["Authority"], tenant);
var authContext = new AuthenticationContext(authority);
var credentials = new UserPasswordCredential(userName, password);
var authResult = await authContext.AcquireTokenAsync(serviceUri, clientID, credentials);
Settings are as follows for tenant and application id
Am I missing some thing can some one help me.
Not sure about why the error says "unknown user type", but I'm pretty sure this doesn't work because you are trying to use ROPC flow with a personal Microsoft account.
Resource Owner Password Credentials (ROPC) flow does not work if:
The user is a personal Microsoft account
The user has MFA enabled
The user's password has expired
It also doesn't work with Guest accounts if I recall correctly.
In general I recommend avoiding this method of authentication and instead redirect the user to the Azure AD login page.
Being unable to use multi-factor authentication is usually a pretty big reason to avoid this flow.
Also I'm pretty sure your serviceUri is wrong.
The parameter should define the API you want the tokens for, not a URL to the Azure AD authorization page.
With .Net Core 3.1 and IdentityServer4, I have successfully set up Twitter sign in.
However, if I already created an account with that same email address (independently of Twitter)... when I click login in with Twitter, it then redirects me back to the identity server External Login page with the following message:
You've successfully authenticated with Twitter. Please enter an email address for this site below and click the Register button to finish logging in.
and a textbox with my twitter email address already filled in: [ myemail#mydomain.com ]
When I click Register I get the error message:
User name 'myemail#mydomain.com' is already taken.
This makes some sense... but it would be really nice if I had the option of connecting the Twitter login to the existing account... Is there any way to do this?
Its up to you in the ExternalController.Callback method in IdentityServer to handle the mapping to existing accounts and to create new accounts for new users.
For example, see this code:
// lookup our user and external provider info
var (user, provider, providerUserId, claims) = FindUserFromExternalProvider(result);
if (user == null)
{
// this might be where you might initiate a custom workflow for user registration
// in this sample we don't show how that would be done, as our sample implementation
// simply auto-provisions new external user
user = AutoProvisionUser(provider, providerUserId, claims);
}
I have followed the instructions like the guide said:
LDAP/Active Directory
and How to use LDAP in ASP.NET Boilerplate (Free Startup Template)
But with no success.
Below is my trial and error:
User Scenario:
Most of the users are from the domain, so those domain users should not see the login page and should be able to auto login the platform.
Some of the users are not domain users, for those who have access to the platform but not belong to the domain should pop out the login page and input username/password to login.
Here is a snap of my authentication code:
If(!HttpConetxt.User.Identity.IsAuthenticated)
{
var domainUserName = System.Web.HttpContext.Current.User.Identity.Name;
var entry = new DirectoryEntry("XXX");
var search = new DirectorySearcher(entry);
search.Filter = "(sameaccountname=)" + domainUserName + ")";
// Check if the user is in domain or not
var result = search.FindOne();
if(result != null)
{
//Domain user, find the mapping user in db and login using the db user
...
}
}
Since the website should support both anonymous and windows authentication, I enabled both authentication method:
And also add [AllowAnonymous] attribute to Login ActionResult.
Per my understanding, the request authentication is performed in global.aspx, So I guess my authentication logic should have something to do with this:
protected void Application_AuthenticateRequest()
{
...
}
But it just seems that I could not put my authentication code in there. Because anyway, I need to use the db user to manage user roles, but in the global.aspx, the UserManager is not even there.
I have tried to add this piece of code into Login ActionResult, but there's a problem: when domain user logs out, it will constantly login as it can not tell if the user is actually logged out or just comes to the website.
So:
Where is the right place to put those authentication code?
How does the Ldap work in this scenario? Does Ldap meet the requirements? I could never get the Ldap work in my project.
I'm attempting to migrate our code for using the (soon to be deprecated) Google Provisioning API to the Admin SDK Directory API, via the .NET client libraries provided by Google.
In the old Provisioning API (via the .NET client library) a call to get the groups for a domain was very simple:
Google.GData.Apps.AppsService apps = new Google.GData.Apps.AppsService(AppDomain, DomainAdminEmail, AdminPassword);
Google.GData.Apps.Groups.GroupsService service = apps.Groups;
AppsExtendedFeed appsFeed = service.RetrieveAllGroups();
Not exactly rocket science, and the only credentials required were the domain, the domain's admin email, and the admin password. As long as you can supply those three parameters, you can get the groups for any domain.
I've been trying for two days to create an equivalent call using the new Admin SDK Directory API (via the new .NET client library), and it's giving me a very hard time. The only way that I've been able to get it to work at all is to create a Service Account for the project associated with the domain's admin email, including generation of a private key file, based on a useful post by mwpreston):
//Create security certificate using private key file and password.
var certificate = new X509Certificate2(pathToPrivateKeyFile, privateKeyPassword, X509KeyStorageFlags.Exportable);
//Create a service credential using the certificate, admin email and API scopes.
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
User = adminUserEmail,
Scopes = scopes
}.FromCertificate(certificate));
//Create Directory Service using the service credential and the application name.
var dirservice = new DirectoryService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = applicationName
});
var groupsListRequest = dirservice.Groups.List();
groupsListRequest.Domain = domain;
domainGroups = groupsListRequest.Execute();
However, because the Service Account is associated with a specific domain (as specified in the admin email account for the Service Account) it can only be used to request groups from that specific domain.
We have clients with thousands of different domains. It's impractical to create a new Service Account for every client's domain (as well as a private key file).
I've searched and searched for a way to call the DirectoryService in a way that uses the same parameters (domain, admin email and password) but I can't find anything. The documentation for the Admin API .NET client library is extremely sparse and is of no help.
The Provisioning API will be deprecated on April 20th 2015, so someone else there must have been faced with this issue. Can anyone help?
You should use "impersonate" (via .setServiceAccountUser("user#example.com")) while constructing credential. Look for examples here.
You can create a project on Google Developer Console and generate a refresh/access token instead. Here's the link - https://developers.google.com/accounts/docs/OAuth2WebServer
I haven't used the client library hence can't help you with the code. You can use the 'list' API which will give you all the groups for all domains of a customer (if you provide the customerId) - https://developers.google.com/admin-sdk/directory/v1/reference/groups/list