I am following a sample from https://github.com/davidebbo/AzureWebsitesSamples
private static ResourceManagementClient _resourceGroupClient;
private static WebSiteManagementClient _websiteClient;
private static AzureEnvironment _environment;
private static DnsManagementClient _dnsClient;
static string _ClientId = Startup.StaticConfig.GetValue<string>("Azure:ClientId");
static string _ClientKey = Startup.StaticConfig.GetValue<string>("Azure:ClientSecret");
static string _TenantId = Startup.StaticConfig.GetValue<string>("Azure:TenantId");
static string _SubscriptionId = Startup.StaticConfig.GetValue<string>("Azure:SubscriptionId");
static string _ResourceGroupName = Startup.StaticConfig.GetValue<string>("Azure:ResourceGroupName");
static string _AppName = Startup.StaticConfig.GetValue<string>("Azure:AppName");
public static string ResourceGroupName { get => _ResourceGroupName; set => _ResourceGroupName = value; }
public static async Task MainAsync()
{
// Set Environment - Choose between Azure public cloud, china cloud and US govt. cloud
_environment = AzureEnvironment.PublicEnvironments[EnvironmentName.AzureCloud];
// Get the credentials
TokenCloudCredentials cloudCreds = await GetCredsFromServicePrincipal();
var tokenCreds = new TokenCredentials(cloudCreds.Token);
//var loggingHandler = new LoggingHandler(new HttpClientHandler());
// Create our own HttpClient so we can do logging
var httpClient = new HttpClient();
// Use the creds to create the clients we need
_resourceGroupClient = new ResourceManagementClient(_environment.GetEndpointAsUri(AzureEnvironment.Endpoint.ResourceManager), tokenCreds );
_resourceGroupClient.SubscriptionId = cloudCreds.SubscriptionId;
_websiteClient = new WebSiteManagementClient(_environment.GetEndpointAsUri(AzureEnvironment.Endpoint.ResourceManager), tokenCreds);
_websiteClient.SubscriptionId = cloudCreds.SubscriptionId;
_dnsClient = new DnsManagementClient(tokenCreds);
AddCustomDomainToSite("mycustomdomain.com");
}
private static async Task<TokenCloudCredentials> GetCredsFromServicePrincipal()
{
// Quick check to make sure we're not running with the default app.config
if (_SubscriptionId[0] == '[')
{
throw new Exception("You need to enter your appSettings in app.config to run this sample");
}
var authority = String.Format("{0}{1}", _environment.Endpoints[AzureEnvironment.Endpoint.ActiveDirectory], _TenantId);
var authContext = new AuthenticationContext(authority);
var credential = new ClientCredential(_ClientId, _ClientKey);
var authResult = await authContext.AcquireTokenAsync(_environment.Endpoints[AzureEnvironment.Endpoint.ActiveDirectoryServiceEndpointResourceId], credential);
return new TokenCloudCredentials(_SubscriptionId, authResult.AccessToken);
}
static void AddCustomDomainToSite(string sDomainName)
{
Domain domain = new Domain();
_websiteClient.Domains.CreateOrUpdateAsync(_ResourceGroupName, "mycustomdomain.com", domain);
}
I am trying to add mycustomdomain.com to my Azure app service. When I execute the code _websiteClient.Domains.CreateOrUpdateAsync(_ResourceGroupName, "mycustomdomain.com", domain);, nothing happens. I do not get any errors, and I do not see the custom domain listed under Custom Domains in my app service.
I have already verified ownership of the domain, and I can add it to my app service via the portal, but I am trying to add it through C#. Can someone please help me?
Did you already check on "_websiteClient.WebApps.CreateOrUpdateHostNameBindingAsync(resourcegroup, appservicename, hostname, binding)"?
The binding i give as parameter is this one:
var binding = new HostNameBinding{CustomHostNameDnsRecordType = CustomHostNameDnsRecordType.CName};
For me this is working to add a custom domain to the app service.
Related
I have multi tenant application where each tenant can use different IdP to authenticate. Below code correctly redirects to IdP but problem is to get back the response to ACS endpoint.
Key is the Configuration method which configures the paths and their authentication:
[assembly: OwinStartup(typeof(SSOSamlDemoASPNET.App_Start.Startup))]
namespace SSOSamlDemoASPNET.App_Start
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.Map("/client/okta", (appx) =>
{
ConfigureAuthentication(appx, "/client/okta/Saml2", ...);
});
app.Map("/client/azuread", (appx) =>
{
ConfigureAuthentication(appx, "/client/azuread/Saml2", ...);
});
}
private static void ConfigureAuthentication(IAppBuilder app, string modulePath, string audience, string issuer, string metadataUrl)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
CookieName = "LoggedUser",
CookiePath = "/",
CookieManager = new SystemWebCookieManager(),
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
ConfigureSaml(app, modulePath, audience, issuer, metadataUrl);
}
private static void ConfigureSaml(IAppBuilder app, string modulePath, string audience, string issuer, string metadataUrl)
{
var saml2options = new Saml2AuthenticationOptions(false);
var spOptions = new SPOptions
{
EntityId = new EntityId(audience),
ModulePath = modulePath,
PublicOrigin = new Uri("https://localhost:44340/"),
};
spOptions.Logger = new ConsoleLoggerAdapter();
saml2options.SPOptions = spOptions;
saml2options.IdentityProviders.Add(new IdentityProvider(new EntityId(issuer), spOptions)
{
AllowUnsolicitedAuthnResponse = true,
MetadataLocation = metadataUrl,
LoadMetadata = true,
Binding = Saml2BindingType.HttpPost,
});
app.UseSaml2Authentication(saml2options);
}
}
}
Authenticating against individual IdP is done like this:
authProperties.Dictionary["idp"] = "https://sts.windows.net/xxx/";
authProperties.RedirectUri = "https://localhost:44340/client/azuread/ExternalLoginCallback";
HttpContext.Current.Request.GetOwinContext().Authentication.Challenge(authProperties, "Saml2");
When inspecting code of the Sustainsys.Saml2 library (especially Saml2AuthenticationHandler). I found the conditions do not take into account OwinRequest.PathBase and therefore the identity is not coming back to the application.
An example can be (Saml2AuthenticationHandler.Invoke method).
Options.SPOptions.ModulePath = /client/azuread/Saml2
Request.Path = /Saml2/Acs
==> therefore the code inside the condition is not executed.
public override async Task<bool> InvokeAsync()
{
var Saml2Path = new PathString(Options.SPOptions.ModulePath);
if (Request.Path.StartsWithSegments(Saml2Path, out PathString remainingPath))
{
if (remainingPath == new PathString("/" + CommandFactory.AcsCommandName))
{
var ticket = (MultipleIdentityAuthenticationTicket)await AuthenticateAsync();
if (ticket.Identities.Any())
{
Context.Authentication.SignIn(ticket.Properties, ticket.Identities.ToArray());
// No need to redirect here. Command result is applied in AuthenticateCoreAsync.
}
else
{
Response.Redirect(ticket.Properties.RedirectUri);
}
return true;
}
Is there any way to change this behavioral? e.g. saml2Options.Notifications to get this working?
That is obviously a bug/lack of feature, but nothing that will be fixed on the Owin module - it's on life support.
The solution for a multi tenancy owin app is to register one Saml2 middleware and add multiple IdentityProviders to that one. The middleware will handle all responses on the same endpoint and use the configuration from the right IdentityProvider based on where the response came from.
I require an access token to test methods in a service. I tried to hardcode the access token and it is working well. If I run the application after some time all test cases get failed. I believe it's because the access token gets expired. How can I generate access tokens without hardcoding?
The following is the way I used in my work.
First portion inject configuration and second is for getting access token. Hope so it will help you.
public class TestConfigurationManager
{
private IConfiguration _config;
public TestConfigurationManager()
{
IServiceCollection services = new ServiceCollection();
services.AddSingleton<IConfiguration>(Configuration);
}
public IConfiguration Configuration
{
get
{
if (_config == null)
{
var builder = new ConfigurationBuilder().AddJsonFile($"testsettings.json", optional: false);
_config = builder.Build();
}
return _config;
}
}
}
[Fact(DisplayName = "GetAccessToken")]
public async Task GetAccessToken()
{
var _configuration = new TestConfigurationManager().Configuration;
var _httpMessageHandler = new Mock<HttpMessageHandler>();
var httpClient = new HttpClient(_httpMessageHandler.Object);
TokenRequest tokenRequest = new TokenRequest();
_configuration.GetSection(AppSetting.Accounting.Token).Bind(tokenRequest);
string baseAddress = $"{_configuration[AppSetting.Accounting.TokenURL]}"; ;
var form = new Dictionary<string, string>
{
{"grant_type", tokenRequest.GrantType},
{"username", tokenRequest.UserName},
{"password", tokenRequest.Password},
{"key", tokenRequest.Key}
};
httpClient = new HttpClient
{
BaseAddress = new System.Uri(_configuration[AppSetting.Accounting.BaseUrl])
};
HttpResponseMessage tokenResponse = await httpClient.PostAsync(baseAddress, new FormUrlEncodedContent(form));
var jsonContent = await tokenResponse.Content.ReadAsStringAsync();
var tok = JsonConvert.DeserializeObject<TokenResponse>(jsonContent);
Assert.NotNull(tok?.AccessToken);
}
When starting up a fresh .Net Core 2.0 project with Azure AD Authentication you get a working sample that can sign in to your tenant, great!
Now I want to get an access token for the signed in user and use that to work with Microsoft Graph API.
I am not finding any documentation on how to achieve this. I just want a simple way to get an access token and access the graph API, using the template created when you start a new .NET Core 2.0 project. From there I should be able to figure out the rest.
Very important that it works with the project that gets created when following the process where you select Work and school accounts for authentication when creating a new 2.0 MVC Core app in Visual Studio.
I wrote a blog article which shows just how to do that: ASP.NET Core 2.0 Azure AD Authentication
The TL;DR is that you should add a handler like this for when you receive an authorization code from AAD:
.AddOpenIdConnect(opts =>
{
Configuration.GetSection("Authentication").Bind(opts);
opts.Events = new OpenIdConnectEvents
{
OnAuthorizationCodeReceived = async ctx =>
{
var request = ctx.HttpContext.Request;
var currentUri = UriHelper.BuildAbsolute(request.Scheme, request.Host, request.PathBase, request.Path);
var credential = new ClientCredential(ctx.Options.ClientId, ctx.Options.ClientSecret);
var distributedCache = ctx.HttpContext.RequestServices.GetRequiredService<IDistributedCache>();
string userId = ctx.Principal.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
var cache = new AdalDistributedTokenCache(distributedCache, userId);
var authContext = new AuthenticationContext(ctx.Options.Authority, cache);
var result = await authContext.AcquireTokenByAuthorizationCodeAsync(
ctx.ProtocolMessage.Code, new Uri(currentUri), credential, ctx.Options.Resource);
ctx.HandleCodeRedemption(result.AccessToken, result.IdToken);
}
};
});
Here my context.Options.Resource is https://graph.microsoft.com (Microsoft Graph), which I'm binding from config along with other settings (client id etc.).
We redeem a token using ADAL, and store the resulting token in a token cache.
The token cache is something you will have to make, here is the example from the example app:
public class AdalDistributedTokenCache : TokenCache
{
private readonly IDistributedCache _cache;
private readonly string _userId;
public AdalDistributedTokenCache(IDistributedCache cache, string userId)
{
_cache = cache;
_userId = userId;
BeforeAccess = BeforeAccessNotification;
AfterAccess = AfterAccessNotification;
}
private string GetCacheKey()
{
return $"{_userId}_TokenCache";
}
private void BeforeAccessNotification(TokenCacheNotificationArgs args)
{
Deserialize(_cache.Get(GetCacheKey()));
}
private void AfterAccessNotification(TokenCacheNotificationArgs args)
{
if (HasStateChanged)
{
_cache.Set(GetCacheKey(), Serialize(), new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(1)
});
HasStateChanged = false;
}
}
}
The token cache here uses a distributed cache to store tokens, so that all instances serving your app have access to the tokens. They are cached per user, so you can retrieve a token for any user later.
Then when you want to get a token and use MS graph, you'd do something like (important stuff in GetAccessTokenAsync()):
[Authorize]
public class HomeController : Controller
{
private static readonly HttpClient Client = new HttpClient();
private readonly IDistributedCache _cache;
private readonly IConfiguration _config;
public HomeController(IDistributedCache cache, IConfiguration config)
{
_cache = cache;
_config = config;
}
[AllowAnonymous]
public IActionResult Index()
{
return View();
}
public async Task<IActionResult> MsGraph()
{
HttpResponseMessage res = await QueryGraphAsync("/me");
ViewBag.GraphResponse = await res.Content.ReadAsStringAsync();
return View();
}
private async Task<HttpResponseMessage> QueryGraphAsync(string relativeUrl)
{
var req = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/v1.0" + relativeUrl);
string accessToken = await GetAccessTokenAsync();
req.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
return await Client.SendAsync(req);
}
private async Task<string> GetAccessTokenAsync()
{
string authority = _config["Authentication:Authority"];
string userId = User.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
var cache = new AdalDistributedTokenCache(_cache, userId);
var authContext = new AuthenticationContext(authority, cache);
string clientId = _config["Authentication:ClientId"];
string clientSecret = _config["Authentication:ClientSecret"];
var credential = new ClientCredential(clientId, clientSecret);
var result = await authContext.AcquireTokenSilentAsync("https://graph.microsoft.com", credential, new UserIdentifier(userId, UserIdentifierType.UniqueId));
return result.AccessToken;
}
}
There we acquire a token silently (using the token cache), and attach it to requests to the Graph.
I have a list of emails.
For each email, I need to create a Google Drive folder and share it with the given email.
How can I do it programmically?
I am using ASP.NET 4.0.
First of all you need to make sure you have application with clientid/secret and correct redirect uri configured. For my case - it's a desktop application:
So far you'll get clientId/secret key:
Now it's time to write some codes!
Step 1 - authorization:
private async static Task<UserCredential> Auth(ClientSecrets clientSecrets)
{
return await GoogleWebAuthorizationBroker.AuthorizeAsync(clientSecrets, Scopes, "user", CancellationToken.None);
}
Step 2 - construct your client for google drive:
private static DriveService GetService(UserCredential credential)
{
return new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "MyApplicationName",
});
}
Step 3 - create folder (or any other content):
private static string CreateFolder(DriveService service, string folderName)
{
var file = new File {Title = folderName, MimeType = "application/vnd.google-apps.folder"};
var result = service.Files.Insert(file).Execute();
return result.Id;
}
Step 4 - share it!
/// <summary>
/// Share content. Doc link: https://developers.google.com/drive/v2/reference/permissions/insert
/// </summary>
private static void Share(DriveService service, string fileId, string value, string type, string role)
{
var permission = new Permission {Value = value, Type = type, Role = role};
service.Permissions.Insert(permission, fileId).Execute();
}
And finally usage of whole thing:
static void Main(string[] args)
{
var ClientId = "MySecredId";
var SecretKey = "MySecretKey";
var Scopes = new[] { DriveService.Scope.DriveFile, DriveService.Scope.Drive };
var secrets = new ClientSecrets { ClientId = ClientId, ClientSecret = SecretKey };
var credentials = Auth(secrets).Result;
var service = GetService(credentials);
var folderId = CreateFolder(service, "folderName");
Share(service, folderId, "user#gmail.com", "user", "reader");
}
For list of emails you can do pretty same thing creating/sharing content in a loop for every email you have.
Some usefull links:
Creating files
Sharing files
Also
You'll need Google.Apis.Drive.v2 nuget package
The steps to perform this hinge on authenticating with Google first. Once you've done that you'll be able to access the Drive API to perform the actions you'd like. The following links walk you through everything you need to do.
Step 1: Authenticate (server side in your case as you're using ASP.NET)
https://developers.google.com/drive/web/auth/web-server
Step 2: Create your folders
https://developers.google.com/drive/web/folder
Step 3: Share your folders
https://developers.google.com/drive/web/manage-sharing
Take a look in below link. It have full course on Google Drive !!
https://www.codeschool.com/courses/discover-drive
I am developing Web Application to Upload a file on Google Drive in ASP.NET using C#.
While running the Application I want to upload file automatically to Google Drive with automatic authentication technique.
Following is the class file used to upload file but unable to authenticate to Google API.There is something Wrong with GetAuthorization(). Please help me for that...
namespace Cloud
{
class Code
{
private static readonly string CLIENT_ID = "303453771054- 4ftp53uun2o6jeiii85i22muc4fio76f.apps.googleusercontent.com";
private static readonly string CLIENT_SECRET = "wMc6mptgMTOL1ncGSJX-qoPS";
private static readonly string APIKEY = "AIzaSyAVyZF8OVl4Vvrbu9pWUQCzx35afdpYJR0";
private static readonly string REDIRECTURI = "http://localhost:49632/CloudCode.aspx";
private static readonly string[] SCOPES = new[] { DriveService.Scopes.Drive.GetStringValue() };
public void Main(string Filename)
{
var provider = new WebServerClient(GoogleAuthenticationServer.Description, CLIENT_ID, CLIENT_SECRET);
provider.ClientIdentifier = CLIENT_ID;
provider.ClientSecret = CLIENT_SECRET;
provider.RequestUserAuthorization();
provider.ProcessUserAuthorization(null);
var authenticator = new OAuth2Authenticator<WebServerClient>(provider, GetAuthorization);
File body = new File();
body.Title = "My document";
body.Description = "A test document";
body.MimeType = "text/plain";
body.OriginalFilename = Filename;
byte[] byteArray = System.IO.File.ReadAllBytes(body.OriginalFilename.ToString());
System.IO.MemoryStream stream = new System.IO.MemoryStream(byteArray);
DriveService service = new DriveService();
FilesResource.InsertMediaUpload request = service.Files.Insert(body, stream, "text/plain");
request.Upload();
File file = request.ResponseBody;
}
private IAuthorizationState GetAuthorization(WebServerClient arg)
{
IAuthorizationState state = arg.ProcessUserAuthorization(new HttpRequestInfo(HttpContext.Current.Request));
IAuthorizationState AuthorizationState=new AuthorizationState(SCOPES);
AuthorizationState.Callback = new Uri(REDIRECTURI);
return AuthorizationState;
}