Using Office Outlook API with hard-code user name and password - authentication

I'm trying to build a (C#) web app that allows clients to make appointments with me.
I'd like the web app to be able to read and add entries to my outlook calendar.
Many users will use the web app, but the web app will only access one outlook calendar - mine.
All of the examples I have been able to get working have involved the web app user interactively authenticating - but my users will not know my password.
I would like to hard code my username/email address and password in the web app.
When trying to acquire a token I get an error:
Microsoft.IdentityModel.Clients.ActiveDirectory.AdalServiceException:
AADSTS65001: The user or administrator has not consented to use the application.
Send an interactive authorization request for this user and resource.
I am not an administrator of the tenant. Is there any way I can get this to work without administrator involvement?
Would using some kind of certificate rather than a user name and password as user credentials help?
My code (currently in a simple C# console application) is as follows:
UserCredential uc = new UserCredential(MyUsername, MyPassword);
var AuthContext = new AuthenticationContext("https://login.windows.net/Common");
// this doesn't work unless an unexpired token already exists
ar = AuthContext.AcquireToken("https://outlook.office365.com/", MyClientId, uc);
// this does work, but requires the app user to know the password
ar = AuthContext.AcquireToken("https://outlook.office365.com/", MyClientId, new Uri(MyReturnURI));

To enable use the username and password to request the token directly, we need to consent to use the app.
We can use the OAuth 2.0 authorization code grant flow to grant the consent by user. Here is an sample use the ADAL authentication library(3.13.1.846) to acquire the delegate token:
static string authority= "https://login.microsoftonline.com/common";
public static string GetDeligateToken(string resource, string clientId,string redirectURL)
{
AuthenticationContext authContext = new AuthenticationContext(authority);
AuthenticationResult authResult= authContext.AcquireTokenAsync(resource, clientId,new Uri(redirectURL), new PlatformParameters(PromptBehavior.Auto)).Result;
return authResult.AccessToken;
}
After we consent the app, now we can use the code in your post to acquire the token.

Related

ASP.Net Core/Blazor Authentication using Authenticator app

I was investigating passwordless authentication for my new Blazor app and most of the examples point to Email based authentication like medium.com authentication.
User registers
Authentication email sent to the email id
User clicks the link in the email to login
Is there a similar process flow where instead of email the Authenticator app is used. Like
User registers
QR code is generated based on email id + key - similar to email link
User scans the QR code and adds to Authenticator app
User login using the code
All future login - user enters the email id and either notification is sent to Authenticator app or code is entered to login.
You can use the GoogleAuthenticator nuget package to implement an authenticator app-based login. You can do something like this to verify the user.
var accountSecretKey = $"{twoFactorSecretCode}-{user.Email}";
var twoFactorAuthenticator = new TwoFactorAuthenticator();
var result = twoFactorAuthenticator
.ValidateTwoFactorPIN(accountSecretKey, profileViewModel.UserInputCode);
You can find more details about the implementation here

IdentityServer4 with LDAP/AD authentication without UI

I'm currently working on a project where I'm trying to set up a service based on IdentityServer4 (https://github.com/IdentityServer/IdentityServer4) that authenticates users by querying a local Active Directory via LDAP.
To achieve that, I also included the IdentityServer4.LdapExtension (https://github.com/Nordes/IdentityServer4.LdapExtension) in my project. The working example from the repository works fine (https://github.com/Nordes/IdentityServer4.LdapExtension/tree/master/Sample/IdentityServer) - but the custom logic is part of the UI, and I need my service to operate without any UI.
Simply adding
.AddLdapUsers<ActiveDirectoryAppUser>(Conf.GetSection("ldap"), UserStore.InMemory)
as described in the documentation does not change the request pipeline, as the provided login/validation methods are never executed - they are only triggered with calls from the UI (AccountController). However, as I said, I don't want to integrate any UI in this service and rather use the interface which the Token-Endpoint already provides (POST request with client_id and client_secret, response with JWT).
Is there a way to integrate LDAP authentication without rewriting big parts that work out-of-the-box as desired?
From your question it sounds like you already have a username and password. Note client_id != username and client_secret != password. client_id is the identity for a client application.
The grant type you are trying to use is called Resource Owner Password when using the authorize endpoint or password when using the token endpoint.
This grant type is used to support legacy systems and is not recommended for new development.
The code that you want to executed to authenticate a user is in LdapUserResourceOwnerPasswordValidator.cs and it should be executed if you pass the correct parameters to the token endpoint:
POST /connect/token
client_id=yourclientid&
client_secret=yourclientsecret&
grant_type=password&
username=yourusername&password=yourusernamespassword
See token endpoint documentation: https://identityserver4.readthedocs.io/en/release/endpoints/token.html
You can use Identity Model to help you make the token request:
var response = await client.RequestPasswordTokenAsync(new PasswordTokenRequest
{
Address = "https://demo.identityserver.io/connect/token",
ClientId = "yourclientid",
ClientSecret = "yourclientsecret",
UserName = "yourusername",
Password = "yourusernamespassword"
});
This is documented here https://identitymodel.readthedocs.io/en/latest/client/token.html

Get UserInfo from ADFS in UWP with ADAL

I am trying to authenticate the user with ADFS and I am using ADAL. Authetication seems to work since I can get the AccessToken. The problem is that looking at the code authResult contains a UserInfo where all properties (for instance GivenName or FamilyName) are null.
AuthenticationContext authContext = null;
AuthenticationResult authResult;
try
{
authContext = new AuthenticationContext(authority, false);
authResult = await authContext.AcquireTokenAsync(resource, clientId, new Uri(returnUri),
new PlatformParameters(PromptBehavior.Auto, false));
}
Those values are null because of ADFS configuration? I noted that decoding the AccessToken returned I can read User information. But I don't think that decoding the JWT Token is the right way to achieve those information. Do you have a better suggestion?
I have also seen people getting information by using claims, but I don't know exactly how to use it on UWP, since all the sample I found used
ClaimsPrincipal claimsPrincipal = System.Threading.Thread.CurrentPrincipal as ClaimsPrincipal;
But System.Threading.Thread is not available on UWP.
Normally, the access_token is used in Oauth and OpenID connect scenarios and intended to be consumed by the resource. To identify the user we should use the id_token( verify the token and extract the claims abut user by decoding the token). Please refer below about the usage of tokens:
id_token: A JWT token used to represent the identity of the user. The
'aud' or audience claim of the id_token matches the client ID of the
native or server application.
access_token: A JWT token used in Oauth and OpenID connect scenarios
and intended to be consumed by the resource. The 'aud' or audience
claim of this token must match the identifier of the resource or Web
API.
refresh_token: This token is submitted in place of collecting user
credentials to provide a single sign on experience. This token is
both issued and consumed by AD FS, and is not readable by clients
or resources.
And you can refer the link below about the native client to web API scenario for ADFS:
AD FS Scenarios for Developers - Native client to Web API
Depending on the ADFS version of your server. If your company is using Windows Server 2012 R2, then it is ADFS 3.0. I did successfully integrate with SSO login created by the admin of company I am working in. You should refer to this article before venturing in : https://learn.microsoft.com/en-us/previous-versions/adfs-windows-server-2012r2/dn660968(v=msdn.10). Note : you don't even need to make a web api of ToDoList.
using only GetAuthorizationHeader() and authenticationContext.AcquireTokenAsync(), you could obtain the token by asking the user to authorize their credentials and decrypt the receive token.
This is sample of code I did:
authority = https://contoso.com/adfs/ls (Endpoint from the ADFS metadata)
resourceURI = https://localhost:44300/ (Relying party, ask your ADFS admin to register)
clientID = it is recommended to use Package.appmanifest's package name from Packaging tab. As long as it is a unique ID.
clientReturnURI = use the following code to obtain the clientReturnURI (also available in the article in the link) :
string clientReturnURI = string.Format("ms-appx-web://Microsoft.AAD.BrokerPlugIn/{0}",WebAuthenticationBroker.GetCurrentApplicationCallbackUri().Host.ToUpper());
AuthenticationContext ac = new AuthenticationContext(Authority_Uri, false);
AuthenticationResult ar = await ac.AcquireTokenAsync(resourceURI, GlobalVar.clientID, new Uri(clientReturnURI), new PlatformParameters(PromptBehavior.Always, true));
var jwt = new JwtSecurityToken(ar.AccessToken);
string unique_name = jwt.Claims.First(c => c.Type == JwtRegisteredClaimNames.UniqueName).Value;
You can replace JwtRegisteredClaimNames.UniqueName with anything else. It depends what info/claims that is available in the access token. You should inspect the available info in the jwt by placing breakpoint at var jwt. Or you can decrypt the access token in the AuthenticationResult.AccessToken in this website :
https://jwt.ms/
Lastly, you need to install certificate from your ADFS admin and install the certificate across your web and UWP server to allow the application able to trust execute the actions.

Get Access token with Azure AD multi-tenant openID authentication

I followed the sample code here to create a MVC web app with Azure AD multi-tenant OpenID authentication. I use the following code to get user sign in.
public void SignIn()
{
if (!Request.IsAuthenticated)
{
HttpContext.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = "/" }, OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}
Now I need to send a web api call which is protected by my Azure AD as well. Currently, before I send the request I use ADAL library to ask users to login again and get the access token like this.
AuthenticationContext ac = new AuthenticationContext(authority);
AuthenticationResult ar = ac.AcquireToken(resourceID, clientID, redirectURI, PromptBehavior.Always);
string accessToken = ar.AccessToken;
However, since the authentication used in the MVC(if the user is from my AD) is the same as the one used to protect the web api. I'm wondering if there is a way to get the access token when user login with this openID authentication so that I can skip the second login with ADAL?
UPDATE:
Following vibronet's answer, I am trying to use the following code to get the token:
string authority = "https://login.windows.net/ucdavisprojecthotmail.onmicrosoft.com";
ClientCredential clientcred = new ClientCredential(clientId, appKey);
AuthenticationContext authContext = new AuthenticationContext(authority);
AuthenticationResult result = authContext.AcquireTokenSilent(resourceID, clientcred, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));
Here, this code is used in an MVC web app and the clienId and appKey is the clientID and key of the web API I want to call. The resoureID is the APP ID URI of the web API obtained in Azure portal.
However, I got this error: Failed to acquire token silently. Call method AcquireToken. Anything I was missing?
Absolutely. Check out https://github.com/AzureADSamples/WebApp-WebAPI-MultiTenant-OpenIdConnect-DotNet, it's like the sample you've been working with but with in addition the access token acquisition and use you are asking about. Also note, AcquireTokenSilent can only work if you have a token in the cache - to be used directly or refreshed. FInally: when you ask for a token, you must specify both the ID fo the resource you want a token for, and the clientID of the application doing the request. In your code, you appear to have used the clientID of the target app. Please refer to the sample I linked above, it shows the exact pattern to be used in this scenario.

How to get the logged in user details(user email id and user name) using Access Token in AAD Single Sign on using java script back-end?

Hi am developing a windows store 8.1 app using C# and xaml
For log in, am authenticating the user with Windows Azure Active directory Single Sign-on using JavaScript back-end.
Once the user is logged in and i have the access token, how to get the logged in user's user email id and Username using the access token in the app?
Anybody please provide me a solution to get the user email using the access token?
If you have the access token then that should contain the user id value. For retrieving the e-mail address you have to query the Graph API to get user details. The full documentation on that is here but in short you should make a get request like below, placing the AccessToken in the Authorization header, after "Bearer ".
GET https://graph.windows.net/contoso.onmicrosoft.com/users/Alex#contoso.onmicrosoft.com?api-version=2013-04-05 HTTP/1.1
Authorization: Bearer eyJ0eX ... FWSXfwtQ
Content-Type: application/json
You can use either user principal name or objectId in the address. Better yet, you can use the Azure AD Graph Client nuget package and call their API to get user information.
Uri servicePointUri = new Uri("https://graph.windows.net");
Uri serviceRoot = new Uri(servicePointUri, "contoso.onmicrosoft.com");
ActiveDirectoryClient activeDirectoryClient = new ActiveDirectoryClient(serviceRoot, accessToken);
IUser user = activeDirectoryClient.Users
.Where(user => user.UserPrincipalName.Equals("alex#contoso.onmicrosoft.com"))
.ExecuteAsync().Result.CurrentPage.ToList().SingleOrDefault();
See here for the full sample.