ASP.net Identity management with Azure, in Web Form - asp.net

I have a Webform ASP.net (in VB.net) that I want to add identity management too. I have added code that allows for the registration of users. But, I am stuck on how to log a user in. My application uses SSO with Azure Active Directory. What I would like to do is, after the user Authenticates with Azure, then use Identity management to see if the user is in the web system and if they have roles.
This site can be accessed by three types of users: 1) Internal folks who would have a local account as well as an Azure AD account, who act as admins for the site 2) Internal folks who authenticate with Azure AD, but will not have local accounts (i.e authenticated but with no roles) 3) External folks who will not authenticate with Azure AD and will not have local accounts
My questions is, how do I use Identity management, after the users authenticates with Azure AD? Here is my code:
Startup.Auth.vb:
Partial Public Class Startup
Dim appSettings = ConfigurationManager.AppSettings
Private realm As String
Private aadInstance As String
Private tenant As String
Private metadata As String
Private authority As String
Public Sub ConfigureAuth(app As IAppBuilder)
Dim appSettings = ConfigurationManager.AppSettings
realm = ConfigurationManager.AppSettings("ida:RPIdentifier")
aadInstance = ConfigurationManager.AppSettings("ida:AADInstance")
tenant = ConfigurationManager.AppSettings("ida:Tenant")
metadata = String.Format("{0}/FederationMetadata/2007-06/FederationMetadata.xml", aadInstance)
authority = String.Format(CultureInfo.InvariantCulture, aadInstance, tenant)
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType)
app.UseCookieAuthentication(New CookieAuthenticationOptions())
Dim authOption As WsFederationAuthenticationOptions = New WsFederationAuthenticationOptions()
Dim fn = Function(context)
context.HandleResponse()
context.Response.Redirect("Home/Error?message=" + context.Exception.Message)
Return Task.FromResult(0)
End Function
Dim auth_not As WsFederationAuthenticationNotifications = New WsFederationAuthenticationNotifications() With {
.AuthenticationFailed = fn
}
Dim auth_opt As WsFederationAuthenticationOptions = New WsFederationAuthenticationOptions() With {
.Wtrealm = realm,
.MetadataAddress = metadata,
.Notifications = auth_not
}
app.UseWsFederationAuthentication(auth_opt)
End Sub
End Class
Default.aspx.vb login click:
Public Sub SignIn()
Dim newAuth As AuthenticationProperties = New AuthenticationProperties()
newAuth.RedirectUri = "/"
HttpContext.Current.GetOwinContext().Authentication.Challenge(newAuth, WsFederationAuthenticationDefaults.AuthenticationType)
End Sub
The problem is, I don't know where to user the UserManager to get the logged in user, after the user gets redirected to our companies log in page, and they are redirected back to the site, with the returned Azure AD Username.

Related

How to send email using service account details only in ASP.NET Core console application?

I have gmail API service account details = client id and service account. How can I just send an email from one id to other without OAuth?
I want to authorize this email sending process with the service account credentials only.
Is there a nuget package that can help fulfill this requirement?
How can I just send an email from one id to other without OAuth?
I assume what you mean is how to send an email with out poping up the Oauth2 consent screen.
Using a service account will allow you to do that, once you have configured the permissions properly in your google workspace account. You grant the service account to preform actions on behalf of one of your domain users. This way the service account can send emails as that user without the user having to consent to that access because you have pre authorized it via google workspace.
The following code will show you how to authorize your application to use a service account.
class Program
{
public static string Base64Encode(string plainText)
{
var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText);
return System.Convert.ToBase64String(plainTextBytes);
}
public static void SendMail()
{
try
{
string ApplicationName = "Gmail API .NET Quickstart";
const string serviceAccount = "xxxx#xxxx-api.iam.gserviceaccount.com";
var certificate = new X509Certificate2(#"c:\XXXX.p12", "notasecret", X509KeyStorageFlags.Exportable);
var gsuiteUser = "YourDomain#YourDomain.com";
var serviceAccountCredentialInitializer = new ServiceAccountCredential.Initializer(serviceAccount)
{
User = gsuiteUser,
Scopes = new[] { GmailService.Scope.GmailSend, GmailService.Scope.GmailLabels }
}.FromCertificate(certificate);
var credential = new ServiceAccountCredential(serviceAccountCredentialInitializer);
if (!credential.RequestAccessTokenAsync(CancellationToken.None).Result)
throw new InvalidOperationException("Access token failed.");
var service = new GmailService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
var mailMessage = new MailMessage();
mailMessage.From = new MailAddress("se#Yourdomain.com");
mailMessage.To.Add("ddddd#hotmail.com");
mailMessage.ReplyToList.Add("se#Yourdomain.com");
mailMessage.Subject = "test";
mailMessage.Body = "<h1>sdf</h1>";
mailMessage.IsBodyHtml = true;
//foreach (System.Net.Mail.Attachment attachment in email.Attachments)
//{
// mailMessage.Attachments.Add(attachment);
//}
var mimeMessage = MimeKit.MimeMessage.CreateFromMailMessage(mailMessage);
var gmailMessage = new Message
{
Raw = Base64Encode(mimeMessage.ToString())
};
Message message1 = new Message();
UsersResource.MessagesResource.SendRequest sendRequest = service.Users.Messages.Send(gmailMessage, "me");
var s = sendRequest.Execute();
Console.WriteLine("Message delivered!");
}
catch (Exception ep)
{
Console.WriteLine(ep.ToString());
}
}
The trick is to remember to set up the domain wide delegation properly and to decide which user the service account is going to be impersonating and to remember to add that email
without google workspace
If you do not have a google workspace account then you can not use service accounts. You may want to consider going though the smtp server instead.

Microsoft Graph API Forbiden

Imports Microsoft.Graph
Imports Azure.Identity
Module Module1
Sub Main()
Dim onlineMeeting = New OnlineMeeting With {
.StartDateTime = DateTimeOffset.Parse("2022-04-23T21:33:30.8546353+00:00"),
.EndDateTime = DateTimeOffset.Parse("2022-04-23T22:03:30.8566356+00:00"),
.Subject = "Application Token Meeting",
.Participants = New MeetingParticipants With {
.Organizer = New MeetingParticipantInfo With {
.Identity = New IdentitySet With {
.User = New Identity With {
.Id = "ba525532-764a-4aa1-8103-066beca0f5a8"
}
}
}
}
}
Dim testing As Task(Of OnlineMeeting) = createMeeting(onlineMeeting)
Console.WriteLine(testing.Status.ToString)
Console.ReadKey()
End Sub
Public Async Function createMeeting(onlineMeeting As OnlineMeeting) As Task(Of OnlineMeeting)
Dim createMeetings As OnlineMeeting = Nothing
Try
Dim clientId As String = "XXXXXXXXX"
Dim clientSecret As String = "XXXXXXXXXXXXX"
Dim tenantId As String = "XXXXXXXXXXX"
Dim options = New TokenCredentialOptions With
{
.AuthorityHost = AzureAuthorityHosts.AzurePublicCloud
}
Dim ClientSecretCredential = New ClientSecretCredential(tenantId, clientId, clientSecret, options)
Dim graphClient As GraphServiceClient = New GraphServiceClient(ClientSecretCredential)
createMeetings = Await graphClient.Communications.OnlineMeetings.Request().AddAsync(onlineMeeting)
Catch ex As ServiceException
Console.WriteLine(ex.RawResponseBody)
End Try
Return createMeetings
End Function
End Module
When i execute this i get :
WaitingForActivation
{"error":{"code":"Forbidden","message":"","innerError":{"request-id":"XXXXX","date":"2021-08-27T11:38:30","client-request-id":"XXXXXXXX"}}}
Can someone help me?
This normally means there is no permission to perform the action.
Check you are including the relavent scope when authenticating against graph. In this case it is 'OnlineMeetings.ReadWrite' for delegated permissions and 'OnlineMeetings.ReadWrite.All' for application permissions
If using delegated permissions check the delegated user has permission to perform this action, if they can't do it manually you can't do it through graph.
If using application permissions make sure you have created an appropriate application access policy as described in the blue note here: https://learn.microsoft.com/en-us/graph/api/application-post-onlinemeetings?view=graph-rest-1.0&tabs=http
Administrators must create an application access policy and grant it to a user, authorizing the app configured in the policy to create an online meeting on behalf of that user (user ID specified in the request path).

Asp.net UseOpenIdConnectAuthentication not working in Azure

I am using UseOpenIdConnectAuthentication to authenticate users. My application code works fine locally. But, when I run it on Azure, the SecurityTokenValidated event is never fired. Consequently, the code runs fine but the user is never authenticated. I am not sure if the issue is with my code or with Azure. This is being used in a Web Form, Asp.net application (not Core). I use the Azure trace feature to log. I can see that only "RedirectToIdentityProvider" is fired. No other event gets called. Here is my code:
Startup.Auth.Vb:
Public Sub ConfigureAuth(app As IAppBuilder)
Dim clientId As String = ""
Dim authority As String = ""
Dim redirectURI As String
Trace.TraceInformation("Hit Config Auth function")
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap = New Dictionary(Of String, String)
app.SetDefaultSignInAsAuthenticationType("Cookies")
app.UseCookieAuthentication(New CookieAuthenticationOptions() With {
.AuthenticationMode = AuthenticationMode.Active,
.CookieManager = New SystemWebCookieManager
})
redirectURI = appSettings("ID_Redirect_URI")
clientId = appSettings("ID_ClientID")
authority = appSettings("ID_Authority")
Trace.TraceInformation(redirectURI)
Trace.TraceInformation(clientId)
Trace.TraceInformation(authority)
Trace.TraceInformation("creating OpenIDAuthOptions")
Dim OpenIdAuthOption = New OpenIdConnectAuthenticationOptions() With {
.SignInAsAuthenticationType = "Cookies",
.Authority = authority,
.RequireHttpsMetadata = False,
.ClientId = clientId,
.ResponseType = "id_token",
.Scope = "openid profile roles",
.RedirectUri = redirectURI,
.PostLogoutRedirectUri = redirectURI,
.Notifications = New OpenIdConnectAuthenticationNotifications() With {
.AuthenticationFailed = Function(ctx)
Trace.TraceInformation("Auth Failed event")
Return Task.FromResult(0)
End Function,
.SecurityTokenReceived = Function(ctx)
Trace.TraceInformation("Sec Token Recieved event")
Return Task.FromResult(0)
End Function,
.MessageReceived = Function(ctx)
Trace.TraceInformation("Message Recieved event")
Return Task.FromResult(0)
End Function,
.SecurityTokenValidated = Function(ctx)
Trace.TraceInformation("Security token validated")
Return Task.FromResult(0)
End Function,
.AuthorizationCodeReceived = Function(ctx)
Trace.TraceInformation("Auth Code Recieved event")
Return Task.FromResult(0)
End Function,
.RedirectToIdentityProvider = Function(context)
Trace.TraceInformation("start of RedirectToIDProvider")
Return Task.FromResult(0)
End Function
}
}
Trace.TraceInformation("adding OpenIdAuthOptyions")
app.UseOpenIdConnectAuthentication(OpenIdAuthOption)
Trace.TraceInformation("finihsed adding OpenIdAuthOptyions")
End Sub
As I mentioned above, this code works fine locally. It only does not work when hosted on Azure. When running locally, the events are fired in this order:
RedirectToIdentityProvider
Message Received
Security Token Received
Security Token Validated
But, in Azure, only RedirectToIdentityProvider is fired.
Changed your Action to take when request is not authenticated in App Service Authentication/Authorization section in the azure portal from LogIn with Azure Active Directory to Allow Anonymous requests. As shown on the picture below:
Then the SecurityTokenValidated would be fired. App services auth takes place outside of you app, so customized auth code in your app never gets a chance to run. When you turn that off it allows your app to handle the auth itself the same way it does locally.
Here is the similar issue you could refer to.
Try changing the application manifest of the application definition on Azure to set the "oauth2AllowIdTokenImplicitFlow" property to true from false.
Go to the Azure Portal,
Select to Azure Active Directory
Select App Registrations
Select your app.
Click on Manifest
Find the value oauth2AllowIdTokenImplicitFlow and change it's value to true
Click Save
2) In your startup.cs file, change the following:
ResponseType = OpenIdConnectResponseType.Code
to
ResponseType = OpenIdConnectResponseType.CodeIdToken
and see if it helps.

Google Calendar API v3 .NET authentication with Service Account or Web Application ID

I need to connect a Google Calendar on my .NET 4.5 application (VS 2013 project).
I want to get all the information from the Calendar, such as: events, dates, notes, names, guests, etc...
I used the Google Developer Console to create both a Web Application Client ID and a Service Account, but I get different errors and no results.
I've implemented 2 different methods, one to login with Web Application Client ID and one to use Service Account.
This is the common ASPX page
public partial class Calendar : System.Web.UI.Page
{
// client_secrets.json path.
private readonly string GoogleOAuth2JsonPath = ConfigurationManager.AppSettings["GoogleOAuth2JsonPath"];
// p12 certificate path.
private readonly string GoogleOAuth2CertificatePath = ConfigurationManager.AppSettings["GoogleOAuth2CertificatePath"];
// #developer... e-mail address.
private readonly string GoogleOAuth2EmailAddress = ConfigurationManager.AppSettings["GoogleOAuth2EmailAddress"];
// certificate password ("notasecret").
private readonly string GoogleOAuth2PrivateKey = ConfigurationManager.AppSettings["GoogleOAuth2PrivateKey"];
// my Google account e-mail address.
private readonly string GoogleAccount = ConfigurationManager.AppSettings["GoogleAccount"];
protected void Page_Load(object sender, EventArgs e)
{
// Enabled one at a time to test
//GoogleLoginWithServiceAccount();
GoogleLoginWithWebApplicationClientId();
}
}
Using Web Application Client ID
I've tried to configure the redirect URIs parameter for the JSON config file, but no URI seems to work. I'm on development environment so I'm using IIS Express on port 44300 (SSL enabled). The error I'm getting is:
Error: redirect_uri_mismatch
Application: CalendarTest
The redirect URI in the request: http://localhost:56549/authorize/ did not match a registered redirect URI.
Request details
scope=https://www.googleapis.com/auth/calendar
response_type=code
redirect_uri=http://localhost:56549/authorize/
access_type=offline
client_id=....apps.googleusercontent
The code
private void GoogleLoginWithWebApplicationClientId()
{
UserCredential credential;
// This example uses the client_secrets.json file for authorization.
// This file can be downloaded from the Google Developers Console
// project.
using (FileStream json = new FileStream(Server.MapPath(GoogleOAuth2JsonPath), FileMode.Open,
FileAccess.Read))
{
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(json).Secrets,
new[] { CalendarService.Scope.Calendar },
"...#developer.gserviceaccount.com", CancellationToken.None,
new FileDataStore("Calendar.Auth.Store")).Result;
}
// Create the service.
CalendarService service = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "CalendarTest"
});
try
{
CalendarListResource.ListRequest listRequest = service.CalendarList.List();
IList<CalendarListEntry> calendarList = listRequest.Execute().Items;
foreach (CalendarListEntry entry in calendarList)
{
txtCalendarList.Text += "[" + entry.Summary + ". Location: " + entry.Location + ", TimeZone: " +
entry.TimeZone + "] ";
}
}
catch (TokenResponseException tre)
{
txtCalendarList.Text = tre.Message;
}
}
Using Service Account (preferred)
I can reach the CalendarListResource.ListRequest listRequest = service.CalendarList.List(); line, so I guess the login works, but then, when I want the list on IList<CalendarListEntry> calendarList = listRequest.Execute().Items; I get the following error:
Error:"unauthorized_client", Description:"Unauthorized client or scope in request.", Uri:""
The code
private void GoogleLoginWithServiceAccount()
{
/*
* From https://developers.google.com/console/help/new/?hl=en_US#generatingoauth2:
* The name of the downloaded private key is the key's thumbprint. When inspecting the key on your computer, or using the key in your application,
* you need to provide the password "notasecret".
* Note that while the password for all Google-issued private keys is the same (notasecret), each key is cryptographically unique.
* GoogleOAuth2PrivateKey = "notasecret".
*/
X509Certificate2 certificate = new X509Certificate2(Server.MapPath(GoogleOAuth2CertificatePath),
GoogleOAuth2PrivateKey, X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(GoogleOAuth2EmailAddress)
{
User = GoogleAccount,
Scopes = new[] { CalendarService.Scope.Calendar }
}.FromCertificate(certificate));
// Create the service.
CalendarService service = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "CalendarTest"
});
try
{
CalendarListResource.ListRequest listRequest = service.CalendarList.List();
IList<CalendarListEntry> calendarList = listRequest.Execute().Items;
foreach (CalendarListEntry entry in calendarList)
{
txtCalendarList.Text += "[" + entry.Summary + ". Location: " + entry.Location + ", TimeZone: " +
entry.TimeZone + "] ";
}
}
catch (TokenResponseException tre)
{
txtCalendarList.Text = tre.Message;
}
}
I prefer the Service Account login because there's no need for user to login with consent screen, since the application should do it by itself each time it needs to refresh. Is it possible to use a Service Account with free Google Account or do I need Admin console? I've read many conflicting reports about that...
Anyway, looking around with Google and also in StackOverflow, I didn't find a solution. I've seen and tried many questions and solutions but with no results. Some examples:
Keep getting Error: redirect_uri_mismatch using youtube api v3
Google Calendar redirect_uri_mismatch
Google API Calender v3 Event Insert via Service Account using Asp.Net MVC
https://developers.google.com/google-apps/calendar/instantiate
https://groups.google.com/forum/#!topic/google-calendar-api/MySzyAXq12Q
Please help! :-)
UPDATE 1 - Using Service Account (preferred) - SOLVED!
The only problem in my code was:
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(GoogleOAuth2EmailAddress)
{
//User = GoogleAccount,
Scopes = new[] { CalendarService.Scope.Calendar }
}.FromCertificate(certificate));
There's NO NEED for User = GoogleAccount.
There is definitely something wrong with your authentication. Here is a copy of my Service account authentication method.
/// <summary>
/// Authenticating to Google using a Service account
/// Documentation: https://developers.google.com/accounts/docs/OAuth2#serviceaccount
/// </summary>
/// <param name="serviceAccountEmail">From Google Developer console https://console.developers.google.com</param>
/// <param name="keyFilePath">Location of the Service account key file downloaded from Google Developer console https://console.developers.google.com</param>
/// <returns></returns>
public static CalendarService AuthenticateServiceAccount(string serviceAccountEmail, string keyFilePath)
{
// check the file exists
if (!File.Exists(keyFilePath))
{
Console.WriteLine("An Error occurred - Key file does not exist");
return null;
}
string[] scopes = new string[] {
CalendarService.Scope.Calendar , // Manage your calendars
CalendarService.Scope.CalendarReadonly // View your Calendars
};
var certificate = new X509Certificate2(keyFilePath, "notasecret", X509KeyStorageFlags.Exportable);
try
{
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = scopes
}.FromCertificate(certificate));
// Create the service.
CalendarService service = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Calendar API Sample",
});
return service;
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException);
return null;
}
}
I have a tutorial on it as well. My tutorial Google Calendar API Authentication with C# The code above was ripped directly from my sample project Google-Dotnet-Samples project on GitHub
Note/headsup: Remember that a service account isn't you. It does now have any calendars when you start you need to create a calendar and insert it into the calendar list before you are going to get any results back. Also you wont be able to see this calendar though the web version of Google Calendar because you cant log in as a service account. Best work around for this is to have the service account grant you permissions to the calendar.

How to get user details in asp.net Windows Authentication

I am using windows Authentication and accessing user name as.
IIdentity winId = HttpContext.Current.User.Identity;
string name = winId.Name;
but i want to get other details like User full name and EmailID.
Since you're on a windows network, then you need to query the Active directory to search for user and then get it's properties such as the email
Here is an example function DisplayUser that given an IIdentity on a windows authenticated network, finds the user's email:
public static void Main() {
DisplayUser(WindowsIdentity.GetCurrent());
Console.ReadKey();
}
public static void DisplayUser(IIdentity id) {
WindowsIdentity winId = id as WindowsIdentity;
if (id == null) {
Console.WriteLine("Identity is not a windows identity");
return;
}
string userInQuestion = winId.Name.Split('\\')[1];
string myDomain = winId.Name.Split('\\')[0]; // this is the domain that the user is in
// the account that this program runs in should be authenticated in there
DirectoryEntry entry = new DirectoryEntry("LDAP://" + myDomain);
DirectorySearcher adSearcher = new DirectorySearcher(entry);
adSearcher.SearchScope = SearchScope.Subtree;
adSearcher.Filter = "(&(objectClass=user)(samaccountname=" + userInQuestion + "))";
SearchResult userObject = adSearcher.FindOne();
if (userObject != null) {
string[] props = new string[] { "title", "mail" };
foreach (string prop in props) {
Console.WriteLine("{0} : {1}", prop, userObject.Properties[prop][0]);
}
}
}
gives this:
Edit: If you get 'bad user/password errors'
The account that the code runs under must have access the users domain. If you run code in asp.net then the web application must be run under an application pool with credentials with domain access. See here for more information
You can define a MyCustomIdentity by overriding from IIdentity and add your own properties etc.
Cast it to the specific Identity, for example WindowsIdentity

Resources