Authentication with AzureAD via TestCafe Tests - testing

I'm unable to authenticate / sign-in via AzureAD when running testCafe.
const testrole = Role(
'https://login.microsoftonline.com/',
async t => {
await t
.typeText(Selector('input').withAttribute('type', 'email'), *******)
.click(Selector('#idSIButton9'))
.typeText(Selector('input').withAttribute('type', 'password'), ********)
.click(Selector('#idSIButton9'));
},
{ preserveUrl: true }
);
The above steps work fine, however after entering the password I get a message saying:
"Unable to sign in to Outlook account, Error: AADSTS900561: The endpoint only accepts POST requests. Received a GET request."
From my initial search, it seems like something to do with 3rd party cookies on the browser. However, I'm unable to find a solution at this time.
Any idea how I get around this issue?

The Azure AD product team has always reminded me that it is a bad idea to try to automate sign in like that.
They will probably detect that you are a bot and start blocking your requests, even if you succeed.
Instead, to acquire access tokens you need to use either the client credentials flow (for app-only tokens) or the resource owner password credentials flow (for delegated user tokens).
Client credentials flow: https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow
ROPC flow: https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth-ropc
You have to take good care to secure the credentials used for testing.
And use a test tenant if possible.

Related

Configure Silent Authentication in Open ID Connect

client type: Spa
grant type: implicit or code(pkce)
As a user, I want to be able to get silently authenticated if I have already logged with my identity provider. If not stay on the client side just like a guest user. And if I want to login to the client I should be able to get authenticated manually through the login page.
This has both manual sign-in and automatic sign-in scenarios. How would you handle such cases in Open ID Connect?
By adding the prompt=none in client settings will silently get a new token if user has a valid session. But if not I want the user to be able to manually authenticate through the login page upon his/her wish.
If I set prompt=none this will never have any user interaction such as authentication.
tags: Silent authentication oidc, automatic login, SSO
It is quite a deep subject, and the flow typically works like this:
CLASSIC OIDC SOLUTION
User is redirected for each SPA
If signed in already at the IDP there is no login prompt
OAuth state is stored in local storage (though it is recommended to only store actual tokens in memory)
When an access token expires (or before) do an iframe token renewal with prompt=none
When a new browser tab is opened do an iframe token renewal to get tokens for that tab - to avoid a full redirect
When the user logs out remove OAuth state from local storage
The most widely used library is OIDC Client which will do a lot of the hard work for you. See also my blog post + code sample for how this looks visually.
PROBLEM AREAS
It is worth being aware also that iframe silent renewal does not work by default in the Safari browser in 2020. Some notes on this here.
Alternatively, you can use signinSilent(). I have used it on my login page ngOnInit (since AuthGuard will anyway redirect the user to login, I thought it will be the perfect place in my scenario).
// login.ts
ngOnInit(): void {
this.authService.signinSilent().then(_ => {}).catch(_ => {});
}
// authService
public signinSilent() {
return this.userManager.signinSilent();
}
signinSilent method will return the user object if user already has a valid session with idp. else it will throw an error, probably login_required.

Identity Server 4, External providers and Web API

I’m in process of developing system which consists from such parts:
- Some services under gateway (Ocelot)
- Mobile client (iOS)
- Identity Server 4
Mobile client hasn’t been prepared yet, so I use Postman for emulating requests from it. My problem is implementation of Authentication with External providers, like Google. It’s my first experience of using IS 4, so I have some misunderstanding and difficulties. Excuse me, if my question is too abstract or if I miss smth obvious.
I successfully deployed IS 4 using all this tutorials and it works with Password Credentials flow in a proper way: I request IS for access token, sending user credentials, it returns token and I can successfully use it for access to my API methods.
Situation with External Providers are different. I’ve overviewed this tutorial (https://learn.microsoft.com/en-us/aspnet/core/security/authentication/social/google-logins?view=aspnetcore-3.1) and some other and add code from it to the IS project. I can successfully log in with Google, using a button on that IS4 web-page which goes with IS 4 Quickstart UI template. But no chance to work with API. As I understand in such workflow client-app should go for a token not to my IS as in example with a local user, but to the Google Auth provider. And I emulated it with Postman and got a strange access_token which has no data and it_token which contains username, email and so on. I try to use this id_token with requests to my API. The result is always 401.
Where I’m wrong? How should I build requests to API with token from Google? Or I have misunderstanding and there should be another flow: client goes to IS with specific request, IS goes to Google and then returns proper token to Client?
Here is configuration of authecation on the side of Web API app:
private void ConfigAuthentication(IServiceCollection services)
{
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
{
options.Authority = "http://localhost:5000";
options.RequireHttpsMetadata = false;
options.Audience = "k_smart_api";
});
}
Here is config of Google-Auth on the side of IdentityServer:
services.AddAuthentication().AddGoogle(opts => {
opts.ClientId = "My google client Id";
opts.ClientSecret = "my google client secret";
opts.SignInScheme = IdentityConstants.ExternalScheme;
opts.SaveTokens = true;
});
This is how I get Access Token:
postman exampple
The tokens you get back from Google, is only used to Authenticate the user in Identity Server. Then after Identity Server receives those tokens, it sign-in the user and create new tokens (ID+access) that are passed to your client. you should look at using the authorization code flow in your client to authenticate the user and to get the tokens. then use the access token received to access your API.
do remember that the tokens received from Google are not used to give access to your APIs.

How to purge chrome.identity cache?

I am developing a Chrome plugin that uses chrome.identity API. There are two distinct flows, one for authenticated users and another for non-authenticated ones. During testing, the problem I'm facing is, if I authenticate the user calling getAuthToken() and then disconnect the connected app from Google account, the plugin still takes the authenticated flow, because I believe chrome.identity framework caches the authentication token. I need to actually wait for some time for getAuthToken() to "realize" that the authentication has been revoked and then the authentication dialog pops up. Is there any way to forcefully purge the chrome.identity cache?
You could use chrome.identity.removeCachedAuthToken. According to documentation:
Removes an OAuth2 access token from the Identity API's token cache.
You have to pass the token you want removed in the first argument, so if you acquired a token with:
var myToken;
chrome.identity.getAuthToken({interactive:true}, function(token){
if (token) myToken = token;
});
You then use:
chrome.identity.removeCachedAuthToken({token:myToken},function(){
//token was removed from cache
});

Laravel 5, how to send API token to mobile app

I have a mobile app which will call a REST API written using Laravel(5.2) framework.
This article on Laravel API authentication mentions how to authenticate users making calls to such an API. The caller should send the correct api_token to the server in the request.
My question is what would be a good way to get the api token to the mobile app? I'm currently thinking of creating a rest api which will authenticate the user based on username and password sent in the request and send the api_token in the response if the user sends a valid username/password pair. Is this method correct/secure? What things should I consider additionally if I do use this method?
You must use one of this methods to have a secure API
JWT TOKEN https://github.com/tymondesigns/jwt-auth
OAUTH2 https://github.com/lucadegasperi/oauth2-server-laravel
With this methods you only send once username and password and you obtain a token that is valid for a time you can decide. But as bigger is the time, more insecure.
To solve this, there are a renew token methods. With a valid token, you can obtain another valid and refresh the old. In this way, the username and password are more protected because they are not sent in every request.
Is not a good idea have the same token for each user all the time, as you saw in the example you provide. It´s very insecure. If someone get this token, he always will can send request in your name. The tokens must have a lifetime.
to answer your question how to send API token to mobile app i will recommend you that your mobile apps get a valid token and after refresh it.
Something as this works great to get a token in your app:
if ( thereAreTokenStored() )
{
if (! theTokenStoredIsValid() )
{
$authentication = refreshToken();
}
}
else
{
$authentication = authenticate();
}
To know all this issues I recommend you this book: https://apisyouwonthate.com/ . I learnt a lot of the 'API WORLD' with this book. It will help you to know all you need to create an API in a professional way and will provide the necessary tools and packages to achieve it and save a lot of work. And you will love your API!!
Yes this approach is safe. Additionally you also need to secure your connection to server by using HTTPS with a SSL certificate.

Google OAuth-2 how to ask user for a password on each login?

I need to ask user for a password each time he using Google OAuth.
There was an option I have used "max_auth_age", but it stops working.
Is there any replacement for this option. If not - could you please suggest where can I submit something like "feature request" to Google to restore this feature.
Thanks.
UPD
I have read possible duplicate topic and tried to use max_age instead max_auth_age. It did not help.
p.s I know that the main idea of OAuth2 not to use any passwords prompts, but its customer requirement. He is afraid that person, who not allowed to use system can have access on shared computer if someone forgot to logout from Gmail.
Aside from BCM and ehsan s' concerns, it is possible to revoke access to your application AND ask for a password on subsequent login attempts.
Following is a NodeJS example with googleapis, but is simple enough to work for all applications:
const google = require('googleapis').google;
const oauth2Client = new google.auth.OAuth2(
'client_id',
'client_secret',
'redirect_uri'
);
// Sign-in code (omitted) here
async function signOut() {
return await oauth2Client.request({
url: 'https://accounts.google.com/Logout',
method: 'GET'
});
}
Unlike oauth2Client.revokeCredentials, requesting https://accounts.google.com/Logout will make google ask for password on subsequent sign-in attempts.
Bare in mind that this request will sign the user out of all google services on the client.
This wont affect other clients on the device however - i.e. sign-out of NodeJS app will not cause the user to be logged out of gmail in Chrome browser running on the same machine and under the same user.
Hope this helps :)