I wanted to send a mail using SMTP server with Azure access token.
I have an application hosted in azure and user login with tenant active directory. I found a code which does the same for google account here.
Can someone help me?
I tried with the below code, generated azure access token and tried to send it to SMTP as below. But something is missing here and going in other way as this code is not sending mail.
public async Task SendmailusingOAuthToken()
{
try
{
string accessToken = await new AppConfig().GetTokenForApplication();
System.Net.Mail.MailMessage message = new System.Net.Mail.MailMessage(
"Anita.Sure#mindtree.com",
"Anita.Sure#mindtree.com",
"NETWORKNET-33499 - " + Guid.NewGuid().ToString(),
"Access to SMTP servers using OAuth");
using (SmtpClient client = new SmtpClient("smtp.office365.com", 587))//, "user1#testaccount1913.narod2.ru", accessToken, true))
{
client.Timeout = 400000;
//client.SecurityMode = SmtpSslSecurityMode.Implicit;
client.EnableSsl = true;
client.SendAsync(message, accessToken);
}
}
catch (Exception ex)
{
throw;
}
}
Token generation code:
public async Task<string> GetTokenForApplication()
{
try
{
string signedInUserID = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
string tenantID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
string userObjectID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
// get a token for the Graph without triggering any user interaction (from the cache, via multi-resource refresh token, etc)
ClientCredential clientcred = new ClientCredential(clientId, appKey);
// initialize AuthenticationContext with the token cache of the currently signed in user, as kept in the app's token filecache
AuthenticationContext authenticationContext = new AuthenticationContext(aadInstance + tenantID, new FileCache(signedInUserID));
AuthenticationResult authenticationResult = await authenticationContext.AcquireTokenSilentAsync(graphResourceID, clientcred,
new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));
return authenticationResult.AccessToken;
}
catch (Exception ex)
{
throw;
}
}
Related
I am developing a game using Monogame and I want to access my Api so that I can make a login but it always returns me an exception. If I use port 80 I get the following one No connection could be made because the target machine actively refused it and if I use port 5000 I get a 401: Not authorized .
By printing in the console I could come to the conclusion that my try is interrupted at the line response = await client.GetStringAsync(builder.Uri.AbsoluteUri);
Is there something wrong with my code?
Communication class
public class Communication
{
private readonly HttpClient client = new HttpClient();
private const string Uri = "http://localhost:5000/";
private const int Port = 5000;
public Communication()
{
}
public async Task<User> Login(string username, string password)
{
string response;
User user = null;
try
{
var builder = new UriBuilder(Uri + "/Api/Account/Login/")
{
Port = Port
};
builder.Query = $"Username={username}&Password={password}";
Debug.WriteLine("Chegou Aqui!!!!")
response = await client.GetStringAsync(builder.Uri.AbsoluteUri);
if (response == "OK")
{
user = JsonConvert.DeserializeObject<User>(response);
}
}
catch (Exception ex)
{
Debug.WriteLine("\tERROR {0}", ex.Message);
}
return user;
}
}
My Api Login Method
[AllowAnonymous]
[HttpPost("Login")]
public IActionResult Authenticate([FromBody]AuthenticateModel userModel)
{
var user = _userService.Authenticate(userModel.Username, userModel.Password);
if(user == null)
{
return BadRequest(new { message = "Username or Password invalid" });
}
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.Name, user.Id.ToString())
}),
Expires = DateTime.UtcNow.AddDays(7),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
var tokenString = tokenHandler.WriteToken(token);
return Ok(new
{
Id = user.Id,
UserName = user.Username,
Token = tokenString
}) ;
}
This article possibly can help you
You can check your firewall settings or if any antivirus installed in your system, make sure to check the settings of it.
You get error 401: Not authorized because HTTP requests by default configured on port 80, not any other ports.
If the problem persists, check your router or switch configuration, if any is in your route to server.
I am having a C# MVC web application through which I'm trying to read the user's group using Microsoft Graph API. But when I'm trying to do so through code using HttpClient I'm getting "403 Forbidden" error.
I have all the required permissions but still getting the error, can't get the reason for the error or any solution for it. I even tried to google it but couldn't find anything.
If anyone can help.
try
{
using (var httpClient = new HttpClient(HttpClientHelper.GetWinHttpHandler()))
{
var json = #"{ 'securityEnabledOnly': true }";
var stringContent = new StringContent(json);
httpClient.DefaultRequestHeaders.Clear();
httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer " + graphapitoken);
httpClient.BaseAddress = new Uri("https://graph.microsoft.com/");
var response = Task.Run(() => httpClient.PostAsync($"v1.0/users/" + UsermailId + "/getMemberGroups", new StringContent(json, Encoding.UTF8, "application/json")));
response.Wait();
if (response.Result.IsSuccessStatusCode)
{
string strResponse = await response.Result.Content.ReadAsStringAsync();
object dec = JsonConvert.DeserializeObject(strResponse);
JObject obj = JObject.Parse(dec.ToString());
List<JToken> obj1 = obj["value"].ToList();
listAssociatedGroups = obj1.Values<string>().ToList();
}
}
}
Getting Token
public class Token
{
public static string GetToken()
{
return GraphToken(ConfigurationManager.AppSettings["ida:Tenant"],ConfigurationManager.AppSettings["ida:ClientId"], ConfigurationManager.AppSettings["ida:ClientSecret"]);
}
private static string GraphToken(string tenantId, string clientId, string clientSecret)
{
AuthenticationContext authContext = new AuthenticationContext("https://login.microsoftonline.com/" + tenantId);
ClientCredential credential = new ClientCredential(clientId, clientSecret);
AuthenticationResult result = authContext.AcquireTokenAsync("https://graph.microsoft.com", credential).GetAwaiter().GetResult();
return result.AccessToken;
}
public static string TokenAsync(string tenantId, string clientId, string clientSecret, string resourceURI)
{
try
{
var authenticationContext = new AuthenticationContext($"https://login.microsoftonline.com/{tenantId}");
ClientCredential credential = new ClientCredential(clientId, clientSecret);
var authenticationResult = authenticationContext.AcquireTokenAsync(resourceURI, credential).GetAwaiter().GetResult();
return authenticationResult.AccessToken;
}
catch (Exception ex)
{
throw new Exception("Failed to retrive AAD token");
}
}
}
API Permissions I have
First, you could test this API with Graph Explorer directly.
POST https://graph.microsoft.com/v1.0/me/getMemberGroups
{
"securityEnabledOnly": true
}
I'm not sure which kind of flows that you used to get access token in your code. If you use client credentials flow, you need to add one of the application permissions. Delegated permissions can be used for the other flows.
Just a stab in the dark:
Some of those permissions require admin consent.
Does your application have admin consent for those permissions?
For an integration test I have an authorized .NET Core 2.2 Controller that is calling another authorized controller (different project) or external api (like Microsoft Graph).
Both apis are authenticated against the Azure AD. In all the controller actions we need the authenticated user.
We can get in the first api by getting a token based on the username and password (grant_type=password). When the call continues to the second api, it breaks because of an interactive login prompt (We use ADAL).
Normally, the user authenticates with open id connect, we then have the authentication code and get the accesstoken + refresh token with the authentication code. With the refresh token we can get an access token for the second api.
We created a small sample project with default Values Controllers to explain our problem.
Get access token before calling the first api with native app registration:
public static async Task<string> AcquireTokenAsync(string username, string password)
{
var aadInstance = "https://login.windows.net/{0}";
var tenantId = "put id here";
var authority = string.Format(aadInstance, tenantId);
var clientId = "clientid here";
var resource = "put resource here";
var client = new HttpClient();
var tokenEndpoint = $"https://login.microsoftonline.com/{tenantId}/oauth2/token";
var body = $"resource={resource}&client_id={clientId}&grant_type=password&username={username}&password={password}";
var stringContent = new StringContent(body, Encoding.UTF8, "application/x-www-form-urlencoded");
var result = await client.PostAsync(tokenEndpoint, stringContent).ContinueWith((response) =>
{
return response.Result.Content.ReadAsStringAsync().Result;
});
JObject jobject = JObject.Parse(result);
var token = jobject["access_token"].Value<string>();
return token;
}
First API:
[Authorize]
[HttpGet]
public async Task<IActionResult> Get()
{
string name = User.Identity.Name;
var result = await AcquireTokenSilentWithImpersonationAsync();
string BaseUrl = "https://localhost:44356/";
var client = new HttpClient
{
BaseAddress = new Uri(BaseUrl)
};
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
var url = "api/values";
HttpResponseMessage response = await client.GetAsync(url);
switch (response.StatusCode)
{
case HttpStatusCode.OK:
int x = 1;
break;
default:
throw new HttpRequestException($"Error - {response.StatusCode} in response with message '{response.RequestMessage}'");
}
return Ok();
}
private const string BackendResource = "Second api resource here";
/// <summary>
/// For more information: https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-devhowto-adal-error-handling
/// </summary>
/// <returns></returns>
public async Task<AuthenticationResult> AcquireTokenSilentWithImpersonationAsync()
{
const string ClientId = "client id of first api here";
const string ClientSecret = "secret of first api here";
ClientCredential credential = new ClientCredential(ClientId, ClientSecret);
string userObjectId = _httpContextAccessor.HttpContext.User.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier")?.Value;
var authContext = GetAuthenticationContext(userObjectId);
AuthenticationResult authResult = null;
try
{
authResult = await authContext.AcquireTokenSilentAsync(BackendResource, credential, new UserIdentifier(userObjectId, UserIdentifierType.UniqueId));
}
catch (AdalSilentTokenAcquisitionException ex)
{
// Exception: AdalSilentTokenAcquisitionException
// Caused when there are no tokens in the cache or a required refresh failed.
// Action: Case 1, resolvable with an interactive request.
try
{
authResult = await authContext.AcquireTokenAsync(BackendResource, ClientId, new Uri("https://backurl.org"), new PlatformParameters(), new UserIdentifier(userObjectId, UserIdentifierType.UniqueId));
}
catch (Exception exs)
{
throw;
}
}
catch (AdalException e)
{
// Exception: AdalException
// Represents a library exception generated by ADAL .NET.
// e.ErrorCode contains the error code.
// Action: Case 2, not resolvable with an interactive request.
// Attempt retry after a timed interval or user action.
// Example Error: network_not_available, default case.
throw;
}
return authResult;
}
Second api:
[Authorize]
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
string name = User.Identity.Name;
return new string[] { "value1", "value2" };
}
You need to use the On-behalf-of flow in your Web API (not the interactive token acquisition, need)
If you want to use ADAL.NET, a sample is there: https://github.com/azure-samples/active-directory-dotnet-webapi-onbehalfof
but I would now recommend you use MSAL.NET. the sample is: active-directory-dotnet-native-aspnetcore-v2/2. Web API now calls Microsoft Graph, and the documentation: https://aka.ms/msal-net-on-behalf-of
Also note that for Web APIs, we don't use OIDC (this is to sign-in users), but rather a JWT bearer middleware
We have a requirement to allow a third party to authenticate to an Azure Web App and display an Azure AD secured Web App View non interactively.
The problem I am encountering is I can get a token, but when I try to request the required resource in Azure Web App with the token, I am getting a Page saying to Sign into my Account instead of the HTML content from Azure Web App.
I had developed the code following steps from the below picture
string aadInstance = "https://login.microsoftonline.com/{0}";
string tenant = "xxxx.onmicrosoft.com";
string clientId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
string authority = String.Format(CultureInfo.InvariantCulture, aadInstance, tenant);
Uri redirectUri = new Uri(#"http://xxxxxDaemonAppDev");
string resourcePath = #"https://xxxxx.azurewebsites.net/Customer/CashSummary?term=xxxxxx";
string appIdURI = #"https://xxxxx.onmicrosoft.com/WebApp-xxxxx.azurewebsites.net";
AuthenticationContext authContext = null;
AuthenticationResult result = null;
authContext = new AuthenticationContext(authority, new FileCache());
UserCredential uc = new UserPasswordCredential("xxxx#jkintranet.com", "xxx#xxxx");
try
{
//I am getting the Token here.
result = authContext.AcquireTokenAsync(appIdURI, clientId, uc).Result;
#region Call Web APP
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
HttpResponseMessage response = httpClient.GetAsync(resourcePath).Result;
if (response.IsSuccessStatusCode)
{
//I am not getting the HTML Content here
string rezstring = response.Content.ReadAsStringAsync().Result;
var todoArray = JArray.Parse(rezstring);
Console.ForegroundColor = ConsoleColor.Green;
foreach (var todo in todoArray)
{
Console.WriteLine(todo["Title"]);
}
}
#endregion
}
catch (Exception ee)
{
MessageBox.Show(ee.Message);
return;
}
Tools and Technologies followed:
Client App is a Daemon or Server Application to Web API
Server App is a Web Azure Web App Secured with Azure AD authentication
Both Server as Web APP and Client as Native are registered in Azure AD
The Architecture I followed:
Following the steps, I have written the code
The Web App's StartupAuth.cs has this:
public void ConfigureAuth(IAppBuilder app)
{
ApplicationDbContext db = new ApplicationDbContext();
AppUserModelContext appUserDB = new AppUserModelContext();
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
//Changed this from
//app.UseCookieAuthentication(new CookieAuthenticationOptions());
//Changed this to
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Cookies",
//added this
CookieSecure = CookieSecureOption.SameAsRequest,
CookieManager = new Microsoft.Owin.Host.SystemWeb.SystemWebChunkingCookieManager()
});
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = Authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
UseTokenLifetime = false,
Notifications = new OpenIdConnectAuthenticationNotifications()
{
// If there is a code in the OpenID Connect response, redeem it for an access token and refresh token, and store those away.
AuthorizationCodeReceived = (context) =>
{
var code = context.Code;
ClientCredential credential = new ClientCredential(clientId, appKey);
string signedInUserID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
AuthenticationContext authContext = new AuthenticationContext(Authority, new ADALTokenCache(signedInUserID));
//AuthenticationResult result = authContext.AcquireTokenByAuthorizationCode(
//code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, graphResourceId);
AuthenticationResult result = authContext.AcquireTokenByAuthorizationCode(
code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, graphResourceId);
return Task.FromResult(0);
},
SecurityTokenValidated = (context) =>
{
var identity = context.AuthenticationTicket.Identity;
var identityName = context.AuthenticationTicket.Identity.FindFirst("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name").Value;
var aIdentity = identityName.Split('#');
var appUser = appUserDB.Find(aIdentity[0]);
if (appUser == null)
{
context.AuthenticationTicket.Properties.RedirectUri = "/Account/SignOut";
}
//Add Claims-Company
context.AuthenticationTicket.Identity.AddClaim(
new System.Security.Claims.Claim(
"http://com.jksb.org/claims/customclaims/company",
"JKB",
null,
"LOCAL AUTHORITY"));
//Add Claims-Business Unit
context.AuthenticationTicket.Identity.AddClaim(
new System.Security.Claims.Claim(
"http://com.jksb.org/claims/customclaims/buid",
appUser.AppBuID,
null,
"LOCAL AUTHORITY"));
return Task.FromResult(0);
},
//added this
AuthenticationFailed = (context) =>
{
if (context.Exception.Message.StartsWith("OICE_20004") || context.Exception.Message.Contains("IDX10311"))
{
context.SkipToNextMiddleware();
return Task.FromResult(0);
}
return Task.FromResult(0);
}
},
}
We want to use AcquireTokenAsync to acquire the tokens on behalf of user using the following syntax
public static async Task<UserTokenCache> GetAccessTokens(string userUniqueId)
{
UserTokenCache cache = null;
AuthenticationContext authContext = null;
ClientCredential credential = new ClientCredential(clientId, appKey);
AuthenticationResult powerBIResult = null;
AuthenticationResult graphResult = null;
bool isAdalException = false;
try
{
authContext = new AuthenticationContext(Startup.Authority, new NaiveSessionCache(userUniqueId));
powerBIResult = await authContext.AcquireTokenSilentAsync(pbiResourceID, credential, new UserIdentifier(userUniqueId, UserIdentifierType.UniqueId));
graphResult = await authContext.AcquireTokenSilentAsync(graphResourceId, credential, new UserIdentifier(userUniqueId, UserIdentifierType.UniqueId));
cache = new UserTokenCache
{
GraphAccessToken = graphResult.AccessToken,
PBIAccessToken = powerBIResult.AccessToken,
PBITokenExpires = powerBIResult.ExpiresOn,
GraphTokenExpires = graphResult.ExpiresOn
};
}
catch (JsonException je)
{
ExceptionLogger.LogInApplicationInsight(je);
HttpContext.Current.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties(),
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
catch (AdalException ae)
{
ExceptionLogger.LogInApplicationInsight(ae);
if (ae.ErrorCode == "failed_to_acquire_token_silently")
{
isAdalException = true;
}
else
{
HttpContext.Current.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties(),
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}
catch (Exception ex)
{
ExceptionLogger.LogInApplicationInsight(ex);
}
if(isAdalException)
{
try
{
string cacheValue = Convert.ToString(cacheManager.get(userUniqueId));
string decryptedCache = CryptographyUtility.Decrypt(cacheValue);
cache = JsonConvert.DeserializeObject<UserTokenCache>(decryptedCache);
UserAssertion pbiAssertion = new UserAssertion(cache.PBIAccessToken, "urn:ietf:params:oauth:grant-type:jwt-bearer", UserProperties.UserName);
UserAssertion graphAssertion = new UserAssertion(cache.GraphAccessToken, "urn:ietf:params:oauth:grant-type:jwt-bearer", UserProperties.UserName);
cache = null;
powerBIResult = await authContext.AcquireTokenAsync(pbiResourceID, credential, pbiAssertion);
graphResult = await authContext.AcquireTokenAsync(graphResourceId, credential, graphAssertion);
cache = new UserTokenCache
{
GraphAccessToken = graphResult.AccessToken,
PBIAccessToken = powerBIResult.AccessToken,
PBITokenExpires = powerBIResult.ExpiresOn,
GraphTokenExpires = graphResult.ExpiresOn
};
} catch (Exception ex)
{
ExceptionLogger.LogInApplicationInsight(ex);
}
}
return cache;
}
If we use cache.PBIAccessToken and cache.GraphAccessToken to calculate user assertions which are used in AcquireTokenAsync method, it is throwing the error that TenantId is mismatch. In this case, what is the token which needs to be used to calculate UserAssertion.
Generally , we usually use AcquireTokenAsync to get the access token and refresh token the first time user login . After that you could get the token silently(invoke AcquireTokenSilentAsync to get the accessToken ), it will get the token from the TokenCache or silently use refreshToken . If access tokens and refresh tokens both expired ,you may get AdalSilentTokenAcquisitionException( Failed to acquire token silently. Call method AcquireToken) .
Then you could catch that exception , and invokingHttpContext.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = "/" },OpenIdConnectAuthenticationDefaults.AuthenticationType); to redirect user to azure ad login page , after login , user call api with AcquireTokenSilentAsync will work since access token/refresh token exist in cache ,and not expired. Your code in if(isAdalException) is useless if i understand your requirement correctly .