Logout from dotnetcore app authenticated by onelogin using oauth - onelogin

I am using below app to connect onelogin through my .Netcore2.2 web app.Onelogin logout from .NetCore 2.2 mvc webapp.
I am using onelogin to authenticate users and logged into the system in dotnetcore2.2. for onelogin i have used OAuth for authentication. user can log in successfully but not able to logout from app as there is no proper documentation provided by onelogin.
what i did is revoke that token successfully then use HttpContext.SignoutAsync() to logout from webapp. but its not working.
Anybody have solution on above problem.
Thanks in advance.
var options = new OAuthOptions
{
AuthorizationEndpoint = "https://demo.onelogin.com/oidc/auth",
UserInformationEndpoint = "https://demo.onelogin.com/oidc/me",
TokenEndpoint = "https://demo.onelogin.com/oidc/token",
CallbackPath = new PathString("/callback"),
ClaimsIssuer = "https://openid-connect.onelogin.com/oidc",
ClientId = "2fljk230-9456-0975-5hgd-02649b659666158745",
ClientSecret = "7856f543awer33948968b0fgh5b82d4c21a1e29fec87365c32341ba72e20g89",
SaveTokens = true,
above is for Onelogin OAuth configuration
var client = new HttpClient();
var formData = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("token", accessToken),
new KeyValuePair<string, string>("token_type_hint", "access_token"),
new KeyValuePair<string, string>("client_id", "2fljk230-9456-0975-5hgd-02649b659666158745"),
new KeyValuePair<string, string>("client_secret", "7856f543awer33948968b0fgh5b82d4c21a1e29fec87365c32341ba72e20g89")
});
var uri = String.Format("https://openid-connect.onelogin.com/oidc/token/revocation");
var res = await client.PostAsync(uri, formData);

There are a couple of things you need to consider here. You have the tokens issued by Onelogin, but you also have a session with Onelogin which is what provides your user with SSO.
To be clear, there is no one clean way to log a user out of Onelogin using OIDC. So my recommendation is;
Kill all tokens locally. This will leave the tokens as valid in Onelogin for a period of time, but they only exist in Onelogin so the risk surface is minimal.
Kill the Onelogin session. You could try sending the user to https://login.ourtesco.com/logout but for belt and braces you'll need this API, which should only be available to you if you have a backend to the app to allow you to protected the clientid/secret appropriately.
https://developers.onelogin.com/api-docs/1/users/log-user-out

Related

How to use asp.net identity cookie auth along with oidcconnect?

I have an ASP.NET MVC 5 application that uses ASP.NET Identity 2/OWIN that has it's own login using the following:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString(AppConfiguration.LoginPath),
Provider = new CookieAuthenticationProvider
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser, int>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentityCallback: (manager, user) => user.GenerateUserIdentityAsync(manager),
getUserIdCallback: (id) => (Int32.Parse(id.GetUserId())))
}
});
In Addition to our own authentication set up above in the startup, we'd like to also introduce authentication using an external app that uses Identity Server 4 (basically just so a user in that app can sso into ours), using open id connect, setting that up after the above code like:
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
Authority = AppConfiguration.ExternalServerAuthority,
ClientId = "xxxxxxxxxx",
ClientSecret = "secret",
RedirectUri = "http://localhost:1045/signin-oidc",
ResponseType = "id_token",
RequireHttpsMetadata = false,
PostLogoutRedirectUri = "http://localhost:1045/signout-callback-oidc",
SignInAsAuthenticationType = "Cookies",
Notifications = new OpenIdConnectAuthenticationNotifications
{
SecurityTokenValidated = async context =>
{
var appAuthManager = DependencyResolver.Current.GetService<IApplicationAuthenticationManager>();
var userManager = DependencyResolver.Current.GetService<IApplicationUserManager>();
var email = context.AuthenticationTicket.Identity.FindFirst("preferred_username");
var user = userManager.FindByName(email.Value, null);
if (user == null)
{
return;
}
await appAuthManager.SignInAsync(user, false, false);
}
}
});
We need to be able to do a SignInAsync when we get the token from the external app because we need the user to be signed in as the actual user in our own app. Part of our problem is also, if we set this up, then whenever a user is not logged in, they are always sent to the other external app to log in if trying to access a resource/page that they must be logged in for - it no longer sends them to our existing login page (which would give them the option of logging in there like they normally would or clicking a link to take them to the other app to log in if they have a user account for that app too). We don't want this because not all our users will be using/have access to this other app, it's really only for some users, mostly to conveniently navigate into our app from the other app without having to separately sign in to ours. So we can't have all unauthorized requests sent to this other app to log in. How can we achieve that? Is there a better way to set that up here?
Edit for more info:
To explain more clearly the pieces here and what needs to happen. There are actually three applications at play. There is our app, an MVC 5 app that has it's own login page, uses owin/asp.net identity for user authentication and to store/manage its users. There is now another app (for another company that wants to work with us), which is a SPA app that authenticates against a separate IdentityServer4 server run by the same company. This SPA app, wants to put a link in it that sends a user to our MVC 5 app without the user having to actually log in to our MVC 5 app - so, they want to SSO into our MVC 5 app (by use setting up our app to use oidc connect to authenticate the user against the SPA's identity server). So when they get to our app, we need to actually log them in as our user....but we have to also make sure that all our users are not sent to this external app to log in when they are not currently logged in to our app because not all our users will have access to this external app. I hope that clears this up.

How to login to a Azure Active Directory as a User in Test-Code?

I'm new to Azure and struggle a little in learning all the functionalities of the Azure Active Directory (AAD), so I hope you can clear some things up for me. Here is what I already did:
I registered a web app which serves as a resource provider and offers different APIs behind a API management service.
The web app has several users and roles in the AAD. Plus, more detailed permissions are set on App-Level. So the AAD doesn't control all permissions of my users.
Users are authenticated by using OAuth 2.0. In practice, this means if a new user tries to login to my app he gets redirected to Microsofts login page, enters username and password and then gets a JWT token from Microsofts authentication server.
Now what I want to do:
I want to write an app running on my build server which tests the user permissions. The app has to be written in C# .NET Core. Now I'm struggling on how to log in as a user from my code, so my question is:
How can i log in as a user from code to AAD and get the JWT token to test the user permissions? Can I do this by just using username / password, or do I need to register my test app in the AAD? What are the best solutions to reach my goals?
Thank you in advance
Juunas' comment already covered most of what is required. Just putting a bit more detail behind it.
You can use MSAL (link) to write a .NET Core application that accesses your API.
Within MSAL, you need to use username password authentication (Resource Owner Password Credentials grant) to acquire a JWT token. Please never use this grant outside your testing application.
Depending on how your app is configured, using just the clientId of the API could be enough. It would however be best practice to register a separate native app.
Some wording to help you along:
ClientId: The id of the client application which is requesting the token.
Scope: The scope of the API you acquire the token for. Should already be configured somewhere in your API. Usually something with the AppId URI. Possible examples could look like:
https://<yourtenant>.onmicrosoft.com/<yourapi>/user_impersonation
https://<clientId-of-API>/.default
...
Authority: Your AAD, e.g. https://login.microsoftonline.com/yourtenant.onmicrosoft.com
Code example for the password grant from the wiki (more examples there):
static async Task GetATokenForGraph()
{
string authority = "https://login.microsoftonline.com/contoso.com";
string[] scopes = new string[] { "user.read" };
PublicClientApplication app = new PublicClientApplication(clientId, authority);
try
{
var securePassword = new SecureString();
foreach (char c in "dummy") // you should fetch the password
securePassword.AppendChar(c); // keystroke by keystroke
result = await app.AcquireTokenByUsernamePasswordAsync(scopes, "joe#contoso.com",
securePassword);
}
catch(MsalException)
{
// See details below
}
Console.WriteLine(result.Account.Username);
}
I actually find out a way to do it in "pure" C# without using the MSAL library, which I had some trouble with. So if you're looking for a solution w/o MSAL, you can do it the way described below.
Prerequisites
A user must exist in the AAD and must not use a Microsoft Account (source in Active Directory must not be "Microsoft Account").
A client application must be registered in the Azure Active Directory. The client app must be granted permissions to the app you want to test. If the client app is of type "Native", no client secret must be provided. If the client app is of type "Web app / api", a client secret must be provided. For testing purposes, its recommended to use an app of type "Native" without a client secret.
There must be no two factor authentication.
C# Code
You can than create a class "JwtFetcher" and use code like this:
public JwtFetcher(string tenantId, string clientId, string resource)
{
this.tenantId = !string.IsNullOrEmpty(tenantId) ? tenantId : throw new ArgumentNullException(nameof(tenantId));
this.clientId = !string.IsNullOrEmpty(clientId) ? clientId : throw new ArgumentNullException(nameof(clientId));
this.resource = !string.IsNullOrEmpty(resource) ? resource : throw new ArgumentNullException(nameof(resource));
}
public async Task<string> GetAccessTokenAsync(string username, string password)
{
var requestContent = this.GetRequestContent(username, password);
var client = new HttpClient
{
BaseAddress = new Uri(ApplicationConstant.Endpoint.BaseUrl)
};
var message = await client.PostAsync(this.tenantId + "/oauth2/token", requestContent).ConfigureAwait(false);
message.EnsureSuccessStatusCode();
var jsonResult = await message.Content.ReadAsStringAsync().ConfigureAwait(false);
dynamic objectResult = JsonConvert.DeserializeObject(jsonResult);
return objectResult.access_token.Value;
}
private FormUrlEncodedContent GetRequestContent(string username, string password)
{
List<KeyValuePair<string, string>> requestParameters = new List<KeyValuePair<string, string>>()
{
new KeyValuePair<string, string>(ApplicationConstant.RequestParameterName.GrantType, ApplicationConstant.RequestParameterValue.GrantTypePassword),
new KeyValuePair<string, string>(ApplicationConstant.RequestParameterName.Username, username),
new KeyValuePair<string, string>(ApplicationConstant.RequestParameterName.Password, password),
new KeyValuePair<string, string>(ApplicationConstant.RequestParameterName.ClientId, this.clientId),
new KeyValuePair<string, string>(ApplicationConstant.RequestParameterName.Resource, this.resource)
};
var httpContent = new FormUrlEncodedContent(requestParameters);
return httpContent;
}
The grant type for this is just "password".

Getting AADSTS50058: A silent sign-in request was sent but no user is signed in issue for on-behalf of user token generation

I am developing a Web API which will be consumed by a client application. The AD Authentication mechanism i used is "Call a web API with the application's permissions".
The client app calls the web api using application permission and in Web API i am trying to consume Azure Rest API to manage Azure Resources. Azure Rest API's are being consumed on behalf of the user by generating a token using user assertion.
We are running into
"AADSTS50058: A silent sign-in request was sent but no user is signed
in issue for on-behalf of user token generation "
when trying to generate an access token.
Does onbehalf of user scenario works when Client app to Web API follows Application Identity with OAuth 2.0 Client Credentials Grant approach and Web API trying to consume Azure REST/ Managment API's on behalf of the client AD App?
ClientCredential clientCredential = new ClientCredential(ConfigurationManager.AppSettings["ida:ClientId"], ConfigurationManager.AppSettings["ida:SecretKey"]);
AuthenticationContext authContext = new AuthenticationContext(authority);
var bootstrapContext = ClaimsPrincipal.Current.Identities.First().BootstrapContext;
string userName = ClaimsPrincipal.Current.FindFirst(ClaimTypes.Upn) != null ? ClaimsPrincipal.Current.FindFirst(ClaimTypes.Upn).Value : ClaimsPrincipal.Current.FindFirst(ClaimTypes.Email).Value;
UserAssertion userAssertion = new UserAssertion(bootstrapContext.ToString(), "urn:ietf:params:oauth:grant-type:jwt-bearer", userName);
AuthenticationResult result = await authContext.AcquireTokenAsync(ConfigurationManager.AppSettings["AzureResourceManagerIdentifier"], clientCredential, userAssertion);
// Get subscriptions to which the user has some kind of access
//string requestUrl =https://management.azure.com/subscriptions?api-version=2014-04-01"
string requestUrl = string.Format("{0}/subscriptions?api-version={1}", ConfigurationManager.AppSettings["AzureResourceManagerUrl"], AzureResourceManagerAPIVersion);
// Make the GET request
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUrl);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
HttpResponseMessage response = client.SendAsync(request).Result;
if (response.IsSuccessStatusCode)
On Behalf Of gets a token to call another API as the user calling this app.
If your API was called by an app using Client Credentials, then there is no user.
So you cannot use On Behalf Of in this case.

Azure AD app-only access tokens for exchange impersonation

I have been following this resource to create an application on azure that authenticates itself with the azure active directory. We want to use the token from this auth interaction with exchange and the EWS managed API to impersonate everyone in our organization, without them having to login.
I have registered an app with our organization on azure and gave it permission to exchange. After creating a certificate and setting up our azure application with it, I can get an app-only access token using the ADAL via the following code...
string authority = "https://login.windows.net/{tenant}/oauth2/authorize";
AuthenticationContext authenticationContext = new AuthenticationContext(authority, false);
var certPath = #"C:\path\to\cert\Cert.pfx";
var certfile = System.IO.File.OpenRead(certPath);
var certificateBytes = new byte[certfile.Length];
certfile.Read(certificateBytes, 0, (int)certfile.Length);
var cert = new X509Certificate2(
certificateBytes,
PRIVATE_KEY_PASSWORD,
X509KeyStorageFlags.Exportable |
X509KeyStorageFlags.MachineKeySet |
X509KeyStorageFlags.PersistKeySet);
ClientAssertionCertificate cac = new ClientAssertionCertificate(CLIENT_ID, cert);
var token = await authenticationContext.AcquireTokenAsync("https://outlook.office365.com/", cac);
With this token the code to interact with the Ews managed API looks like this..
ServicePointManager.ServerCertificateValidationCallback = CertificateValidationCallBack;
_ExchangeService = new ExchangeService(ExchangeVersion.Exchange2013_SP1) {
Credentials = new OAuthCredentials(token),
Url = new Uri("https://outlook.office365.com/ews/exchange.asmx"),
ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.PrincipalName, "me#email.com"),
};
_ExchangeService.HttpHeaders.Add("X-AnchorMailbox", "me#email.com");
This appears to be the correct way to set up impersonation via the managed api, though every request returns a 401 unauthorized error.
My question boils down to, am I doing something wrong here? Or is there something else that I need to do to give my application access to the exchange server?
The article I was following did mention a client consent flow, but the details around that section aren't clear. Can I not give my app permission for everyone without prompting them for consent initially.
It is working on my side , please firstly confirm you have set Use Exchange Web Services with full access to all mailboxes application permission for Office 365 Exchange Online to your app which protected by Azure AD :
For testing , you could try below code :
ExchangeService exchangeService = new ExchangeService(ExchangeVersion.Exchange2013);
exchangeService.Url = new Uri("https://outlook.office365.com/ews/exchange.asmx");
exchangeService.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, "sp.tech#o365e3w15.onmicrosoft.com");
exchangeService.HttpHeaders.Add("X-AnchorMailbox", "sp.tech#o365e3w15.onmicrosoft.com");
exchangeService.TraceEnabled = true;
exchangeService.TraceFlags = TraceFlags.All;
exchangeService.Credentials = new OAuthCredentials(token.AccessToken);
Folder newFolder = new Folder(exchangeService);
newFolder.DisplayName = "TestFolder";
newFolder.Save(WellKnownFolderName.Inbox);
Which will create a new folder in the target account's Inbox .
I have a working solution with almost the same code.
Only difference that I can ascertain is the authentication context URL, which is https://login.microsoftonline.com/*tenant-id* .
Also, I use new ImpersonatedUserId(ConnectingIdType.SmtpAddress, email);
Are you sure that me#email.com is the full sign-in name, or is it just the e-mail address?

Azure AD Sign In

I have a web application that is secured by Azure AD. I would like to be able to allow some people to access this application. I have created an account in my directory for these users and I would like to log them in without doing a redirect to Azure AD.
Is there any way to get an Azure auth cookie and allow them to access my application without redirecting them to a login? I know the username / password and would like to be able to do the sign in behind the scenes.
You should be able to use the Resource Owner Credentials flow. Assuming you're using ADAL, you can leverage this sample app to retrieve a token.
Once you have the authentication result, you can use it to build an identity and pass that to the cookie authentication manager (assuming you're using the OWIN cookie authentication middleware).
var claims = new List<Claim>();
claims.Add(new Claim(ClaimTypes.GivenName, result.UserInfo.GivenName));
var id = new ClaimsIdentity(claims,
DefaultAuthenticationTypes.ApplicationCookie);
var ctx = Request.GetOwinContext();
var authenticationManager = ctx.Authentication;
authenticationManager.SignIn(id);
Source: http://brockallen.com/2013/10/24/a-primer-on-owin-cookie-authentication-middleware-for-the-asp-net-developer/

Resources