The current settings for the email server is checked into version control.
<configuration>
<system.net>
<mailSettings>
<smtp deliveryMethod="network">
<network host="..." port="25" password="..." userName="..." />
</smtp>
</mailSettings>
</system.net>
</configuration>
I'd like to remove (at least the password) from version control so other developers can't (accidently) send mail over our mail server.
We're using a SAAS provider for our outbound email, because of this I cannot limit access to this server by setting up a firewall. The only authentication is using username and password provided by the SAAS provider.
Is it possible to have the mailSettings be set from the Application settings blade in Azure. This way I can remove the password from version control and do not need to manually update the web.config upon (automatic) deployment.
As you mentioned you are using Postal. On their GitHub, I notice you can provide a function that creates a SmtpClient.
Starting from there, you can create a custom SmtpClient that will be built using application settings:
var smtpServerHost = CloudConfigurationManager.GetSetting("SmtpServerHost");
var smtpServerPort = int.Parse(CloudConfigurationManager.GetSetting("SmtpServerPort"));
var smtpServerUserName = CloudConfigurationManager.GetSetting("SmtpServerUserName");
var smtpServerPassword = CloudConfigurationManager.GetSetting("SmtpServerPassword");
var client = new SmtpClient(smtpServerHost, smtpServerPort);
client.Credentials = new NetworkCredential(smtpServerUserName, smtpServerPassword);
This way you'll be able to modify your email settings in the Application settings of your Azure App Service.
Related
I was starting a brand new project using .Net 5 and Azure ADB2C, but then .Net 6 came out so we decided to go ahead and re-create the small amount of code we had in .Net 6. One thing that was working but now it isn't anymore is the Sign Out functionality. I made no changes on the Azure AD B2C side of things.. On the .Net 5 project all I have to do is send the user to "MicrosoftIdentity/Account/SignOut" to sign them out and that was working as long as I had the following code to send the user back to the home page (sign in page) automatically:
services.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
// Configures the Sign Out action to redirect back to the home page, which will navigate to the login page
options.Events.OnSignedOutCallbackRedirect = async context =>
{
context.HttpContext.Response.Redirect(context.Options.SignedOutRedirectUri);
context.HandleResponse();
};
}
On the .Net 6 version I kept all my code very similarly (even the packages are the same) so I'm not sure why that URL stopped working. After doing some research I found this MS documentation: https://learn.microsoft.com/en-us/azure/active-directory-b2c/openid-connect#send-a-sign-out-request to create a special URL with Azure ADB2C for "end_session_endpoint" but when I navigate to it, it doesn't do anything besides redirecting me back to the application still signed in.
Here are the Packages I'm using:
<PackageReference Include="Microsoft.Graph" Version="4.10.0" />
<PackageReference Include="Microsoft.Identity.Web" Version="1.20.0" />
<PackageReference Include="Microsoft.Identity.Web.UI" Version="1.20.0" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="6.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
And here's a sample of what I have on my Program.cs:
var builder = WebApplication.CreateBuilder(args);
// Set static properties to hold Configuration and Environment
ConfigurationHelper.Configuration = builder.Configuration;
ConfigurationHelper.Environment = builder.Environment;
// Add services to the container.
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAdB2C"));
builder.Services.AddControllersWithViews().AddMicrosoftIdentityUI();
builder.Services.AddAuthorization(options =>
{
// By default, all incoming requests will be authorized according to the default policy
options.FallbackPolicy = options.DefaultPolicy;
});
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor().AddMicrosoftIdentityConsentHandler();
var app = builder.Build();
...
After fixing other unrelated issues, sending the user to the URL "MicrosoftIdentity/Account/SignOut" is working. As I understand the Microsoft.Identity.Web.UI is running the sign out code for me, and the code I have on startup can hookup the sign out callback event to redirect the user. I still think it's weird that the Azure AD B2C "end_session_endpoint" wasn't working for me but maybe it's something related to how Microsoft.Identity.Web manages the session.
Because the old connect to CRM Online is deprecated, we have to use the new method for CRM 2017 Online.
The following procedure runs well at the local development system, but not at the target area, which is a Azure Web App:
CrmConnectionString = "AuthType=Office365; Url=https://<Org>.api.crm4.dynamics.com; Username=<User>; Password=<PW>;"
CrmServiceClient conn = new CrmServiceClient(CrmConnectionString);
Also tried:
CrmServiceClient conn =
new CrmServiceClient(
"<User>",
CrmServiceClient.MakeSecureString("<PW>"),
string.Empty,
"<Org>",
useUniqueInstance: false, useSsl: true, isOffice365: true);
Based on https://community.dynamics.com/crm/f/117/t/188785 also added to web.config:
<system.net>
<defaultProxy useDefaultCredentials="true"/>
</system.net>
Also try from https://blog.vertica.dk/2016/03/04/crm-2016-unable-to-login-to-dynamics-crm/:
WEBSITE_LOAD_USER_PROFILE=1 at appsettings
Nothing happens.
The authentication data and addresses are correct. It's only failed in the Azure enviroment.
Any solutions?
According to Kudu Configurable settings, WEBSITE_LOAD_USER_PROFILE is only available for sites running in Basic or Standard mode. Please check which Web App Plan pricing plan did you choose?
You can also check whether the WEBSITE_LOAD_USER_PROFILE has been applied successfully using Azure Kudu site. On CMD page, redirect to Users folder using 'CD D:\Users' command, you will see a user which has a same name as your web app. If the specific user doesn't not exists, it means the USER_PROFILE has not been loaded.
CD D:\Users
I am coding an integration that has to call Sharepoint-online API's. My integration is not a webapp and has to work without a user present.
As I understand it I need two setup steps:
1. User has to log in to Azure and set up an application and obtain a client ID.
2. I have to call a service with client ID and username and password I will then obtain an Access Token, Refresh Token and ID Token
Once the two setup steps are complete I then can call the service using the access token, but sometimes this will expire and I need to use the refresh token to get a new one.
Step 2 seems odd to me. Why isn't there a user interface where a user can log in and obtain the Access Refresh and ID tokens? Has someone built a utility website that just does this, or have I mis-understood something?
Thanks
Robert
The recommended OAuth flow for service and daemons apps is the Client Credential Flow (in that flow, there no refresh tokens involved; a client ID and a client secret is used to obtain an access token which eventually expires and then you need to get a new access token using the same client ID and secret). In the case of SharePoint Online, you have 2 options for this scenario:
SharePoint Online + Azure Access Control Service (ACS) integration. Details here. In short, you create a service principal (add in only policy) for instance at the site collection level - follow the "Creating the AppPrincipal" section in the blog I linked for this. Then you need to assign the specific permissions your app will need, in the application manifest. See a sample for that in the "Giving the App Principal Permissions" sections - again, you should first define what permissions your app needs. Then, you can use the service principal from a console application:
Program.cs
static void Main(string[] args)
{
Uri siteUri = new Uri("https://tenant.sharepoint.com/teams/test");
//Get the realm for the URL
string realm = TokenHelper.GetRealmFromTargetUrl(siteUri);
//Get the access token for the URL.
// Requires this app to be registered with the tenant
string accessToken = TokenHelper.GetAppOnlyAccessToken(
TokenHelper.SharePointPrincipal,
siteUri.Authority, realm).AccessToken;
HttpWebRequest endpointRequest =
(HttpWebRequest)HttpWebRequest.Create(
"https://tenant.sharepoint.com/teams/test/_api/web/lists/GetByTitle('Documents')/items");
endpointRequest.Method = "GET";
endpointRequest.Accept = "application/json;odata=verbose";
endpointRequest.Headers.Add("Authorization", "Bearer " + accessToken);
HttpWebResponse endpointResponse =
(HttpWebResponse)endpointRequest.GetResponse();
}
}
app.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup>
<appSettings>
<add key="ClientId" value="65e674ca-3827-4134-852b-1196ff935e08"/>
<add key="ClientSecret" value="xxxxxxx"/>
</appSettings>
</configuration>
SharePoint Online + Azure Active Directory (AAD) integration. Details here. In that link you will find a sample code. The difference between the first approach is that in this one you are not using ACS but AAD. The permission that the app needs is defined in AAD - as of today, as far as I know, the application permissions that you can define in AAD are not as granular as the ones you can define via ACS - i.e. with ACS you can define an app at the site collection level, with AAD you can't the app will have tenant wide permissions (i.e. all site collections)
In Module 2 of the MVA course Building Blocks and Services of the SharePoint Platform titled ‘Deep Dive into SharePoint Lists for Data Storage’ (at about 45 minutes in) Ted Pattison did a demo of using a console app to create a list on a SharePoint Online site. The course is at http://www.microsoftvirtualacademy.com/training-courses/deep-dive-building-blocks-and-services-of-sharepoint
I’m trying to do the same in my environment but I’m having trouble.
In the demo he went to _layouts/15/AppRegNew.aspx to register a new app in the app registry. In the demo, at the top of the page there was an ‘App Type’ radio Button list with the options ‘An app running on the web Server’ and ‘An App running on a client machine’. When I access this page on my site, there is no such radio Button List. Also in the demo, Ted left the Redirect URL blank. On my site it is required:
So to get past this I entered the URL for my site (https://mydomain.sharepoint.com/sites/test). The app ID was created successfully:
I then went to _layouts/15/AppInv.aspx to give the app security. I pasted in the CAML to give the app read access to the web:
<AppPermissionRequests>
<AppPermissionRequest Scope="http://sharepoint/content/sitecollection/web" Right="Read" />
</AppPermissionRequests>
And then trusted the app by Clicking Trust It:
I then copied the values from the App registration into my app.config:
<add key="targetSiteUrl" value="https://xxxxx.sharepoint.com/sites/test"/>
<add key="clientId" value="bf4c37ef-9202-41ba-8430-3983cba26285"/>
<add key="clientSecret" value="nKGefHSvT69Ls2rwq1AIVyyHkIwlBzT9UkpbJMUcIbw="/>
<add key="deleteOnly" value="false"/>
And then created code based on what was in the demo to get the webs title:
static void Main(string[] args)
{
string siteUrl = ConfigurationManager.AppSettings["targetSiteUrl"];
bool deleteOnly = ConfigurationManager.AppSettings["deleteOnly"].Equals("true");
Uri siteUri = new Uri(siteUrl);
string realm = TokenHelper.GetRealmFromTargetUrl(siteUri);
var accessToken = TokenHelper.GetAppOnlyAccessToken(TokenHelper.SharePointPrincipal,
siteUri.Authority, realm).AccessToken;
using (var clientContext = TokenHelper.GetClientContextWithAccessToken(siteUrl, accessToken)) {
var web = clientContext.Web;
clientContext.Load(web);
clientContext.ExecuteQuery();
Console.WriteLine(web.Title);
}
}
The code above gets the realm and the access token and successfully creates the clientContext, But when I run the executeQuery I always get the error Microsoft.SharePoint.Client.ServerUnauthorizedAccessException. I have tried giving the app ID full control of the Web, the Site Collection and the Tenant, but I still get the same error.
How do I get my console app to have update access to my site?
I needed to set AllowAppOnlyPolicy when adding the permissions in appinv.aspx
<AppPermissionRequests AllowAppOnlyPolicy="true" >
I have an ASP.NET Web API service that runs on a web server with Windows Authentication enabled.
I have a client site built on MVC4 that runs in a different site on the same web server that uses the HttpClient to pull data from the service. This client site runs with identity impersonation enabled and also uses windows authentication.
The web server is Windows Server 2008 R2 with IIS 7.5.
The challenge I am having is getting the HttpClient to pass the current windows user as part of its authentication process. I have configured the HttpClient in this manner:
var clientHandler = new HttpClientHandler();
clientHandler.UseDefaultCredentials = true;
clientHandler.PreAuthenticate = true;
clientHandler.ClientCertificateOptions = ClientCertificateOption.Automatic;
var httpClient = new HttpClient(clientHandler);
My understanding is that running the site with identity impersonation enabled and then building the client in this manner should result in the client authenticating to the service using the impersonated identity of the currently logged in user.
This is not happening. In fact, the client doesn't seem to be authenticating at all.
The service is configured to use windows authentication and this seems to work perfectly. I can go to http://server/api/shippers in my web browser and be prompted for windows authentication, once entered I receive the data requested.
In the IIS logs I see the API requests being received with no authentication and receiving a 401 challenge response.
Documentation on this one seems to be sparse.
I need some insight into what could be wrong or another way to use windows authentication with this application.
Thank You,
Craig
I have investigated the source code of HttpClientHandler (the latest version I was able to get my hands on) and this is what can be found in SendAsync method:
// BeginGetResponse/BeginGetRequestStream have a lot of setup work to do before becoming async
// (proxy, dns, connection pooling, etc). Run these on a separate thread.
// Do not provide a cancellation token; if this helper task could be canceled before starting then
// nobody would complete the tcs.
Task.Factory.StartNew(startRequest, state);
Now if you check within your code the value of SecurityContext.IsWindowsIdentityFlowSuppressed() you will most probably get true. In result the StartRequest method is executed in new thread with the credentials of the asp.net process (not the credentials of the impersonated user).
There are two possible ways out of this. If you have access to yours server aspnet_config.config, you should set following settings (setting those in web.config seems to have no effect):
<legacyImpersonationPolicy enabled="false"/>
<alwaysFlowImpersonationPolicy enabled="true"/>
If you can't change the aspnet_config.config you will have to create your own HttpClientHandler to support this scenario.
UPDATE REGARDING THE USAGE OF FQDN
The issue you have hit here is a feature in Windows that is designed to protect against "reflection attacks". To work around this you need to whitelist the domain you are trying to access on the machine that is trying to access the server. Follow below steps:
Go to Start --> Run --> regedit
Locate HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0 registry key.
Right-click on it, choose New and then Multi-String Value.
Type BackConnectionHostNames (ENTER).
Right-click just created value and choose Modify.
Put the host name(s) for the site(s) that are on the local computer in the value box and click OK (each host name/FQDN needs to be on it's own line, no wildcards, the name must be exact match).
Save everything and restart the machine
You can read full KB article regarding the issue here.
I was also having this same problem. Thanks to the research done by #tpeczek, I developed the following solution: instead of using the HttpClient (which creates threads and sends requests async,) I used the WebClient class which issues requests on the same thread. Doing so enables me to pass on the user's identity to WebAPI from another ASP.NET application.
The obvious downside is that this will not work async.
var wi = (WindowsIdentity)HttpContext.User.Identity;
var wic = wi.Impersonate();
try
{
var data = JsonConvert.SerializeObject(new
{
Property1 = 1,
Property2 = "blah"
});
using (var client = new WebClient { UseDefaultCredentials = true })
{
client.Headers.Add(HttpRequestHeader.ContentType, "application/json; charset=utf-8");
client.UploadData("http://url/api/controller", "POST", Encoding.UTF8.GetBytes(data));
}
}
catch (Exception exc)
{
// handle exception
}
finally
{
wic.Undo();
}
Note: Requires NuGet package: Newtonsoft.Json, which is the same JSON serializer WebAPI uses.
The reason why this is not working is because you need double hop authentication.
The first hop is the web server, getting impersonation with Windows authentication to work there is no problem. But when using HttpClient or WebClient to authenticate you to another server, the web server needs to run on an account that has permission to do the necessary delegation.
See the following for more details:
http://blogs.technet.com/b/askds/archive/2008/06/13/understanding-kerberos-double-hop.aspx
Fix using the "setspn" command:
http://www.phishthis.com/2009/10/24/how-to-configure-ad-sql-and-iis-for-two-hop-kerberos-authentication-2/
(You will need sufficient access rights to perform these operations.)
Just consider what would happen if any server was allowed to forward your credentials as it pleases... To avoid this security issue, the domain controller needs to know which accounts are allowed to perform the delegation.
To impersonate the original (authenticated) user, use the following configuration in the Web.config file:
<authentication mode="Windows" />
<identity impersonate="true" />
With this configuration, ASP.NET always impersonates the authenticated user, and all resource access is performed using the authenticated user's security context.