Is there a way to trigger login page on app load? - azure-active-directory

Is there a way to trigger the Azure Active Directory sign-in page when an app is first loaded?
I currently have some authentication code on an Angular 11 intranet site that uses APP_INITIALIZER to get the user's Windows credentials when the user first hits the URL, and from there, pass that data onto an API that is able to authenticate the user and get the required information.
Calls to the API are intercepted using an interceptor, and adding a withCredentials field in the request in order for this to happen.
I am currently looking into replacing this functionality with something similar using Azure AD, as we are moving from IIS hosted web servers, to hosting on an Azure App Service. As far as I know, Windows authentication via the browser does not work once hosted on Azure, so the next best option seems to be Azure AD, as it will still contain employee data similar to Windows login (using organization sign-ins).
I've had a look at the following so far:
The code samples by Microsoft contain information on using MSAL v2, but does not have any example showing how I can immediately prompt the user to sign-in, instead of having to click on a login button first. The closest I've been able to get it to work is to add this.login() at the end of the ngOnInit() function in app.component.ts, but this feels rather quite like a hack, and isn't really doing it properly like APP_INITIALIZER.
This example here does show me how to create an app that prompts the user to sign-in on load, but it using the much older MSAL v1 library. If possible, I'd like to go with v2, in the event that v1 is eventually deprecated
I am open to any ideas that helps keep me on Windows authentication, but will also be happy to make the move to Azure AD.

In case someone lands here while Googling.
The solution is to simply add MSALGuard to the default home route. This will force a login via Azure AD.

I know that by now you have solved the issue, but what worked for me was in fact your solution, just adding the MSALGuard under the app-routing-module did the trick.
import { NgModule } from '#angular/core';
import { RouterModule, Routes } from '#angular/router';
import { MsalGuard } from '#azure/msal-angular';
import { BrowserUtils } from '#azure/msal-browser';
import {FormsModule, ReactiveFormsModule} from "#angular/forms";
import {AppComponent} from "./app.component";
const routes: Routes = [
{
path: '',
component: AppComponent,
canActivate: [MsalGuard]
},
];
const isIframe = window !== window.parent && !window.opener;
#NgModule({
imports: [RouterModule.forRoot(routes, { initialNavigation: !BrowserUtils.isInIframe() && !BrowserUtils.isInPopup() ? 'enabledNonBlocking' : 'disabled'}), FormsModule, ReactiveFormsModule],
exports: [RouterModule]
})
export class AppRoutingModule { }
Also, I ensured to use a redirect instead of the popup so that those don't get blocked on page load.
Article referenced for redirects, here.

Related

using react-native-app-auth in react native app and login using MS API for login creates a token in browser

I'm using react-native-app-auth package to do my sign in process with MS Graph API.
The problem that I face here is,
When we do the login using,
import { authorize } from 'react-native-app-auth';
const config = {
issuer: '<YOUR_ISSUER_URL>',
clientId: '<YOUR_CLIENT_ID>',
redirectUrl: '<YOUR_REDIRECT_URL>',
scopes: ['<YOUR_SCOPES_ARRAY>'],
};
const result = await authorize(config);
And this opens the MS Authentication page in an in-app browser(from the react native app) and there it asks for username/password and does the authorization and returns back to the app(react native) with the access token.
Problem:
When I open the safari browser and open office.com or outlook.com and click on sign in it automatically takes to the home page without asking for credentials.
But we don't want this to happen. Any idea to prevent or sort this out?
Thanks in advance.
Regards,
RJ
You can try using Oauth authentication to login for react native app , sample code , if you want to know more , please check - https://github.com/microsoftgraph/msgraph-training-react-native
import AsyncStorage from '#react-native-async-storage/async-storage';
import {authorize, refresh, AuthConfiguration} from 'react-native-app-auth';
import moment from 'moment';
import {AuthConfig} from './AuthConfig';
const config: AuthConfiguration = {
clientId: AuthConfig.appId,
redirectUrl: 'graph-tutorial://react-native-auth/',
scopes: AuthConfig.appScopes,
additionalParameters: {prompt: 'select_account'},
serviceConfiguration: {
authorizationEndpoint:
'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',
tokenEndpoint: 'https://login.microsoftonline.com/common/oauth2/v2.0/token',
},
};
After a struggle was able to understand that, as per the latest updates on security SSO is made mandatory where if we use the default browser or system browser it will store the cookies and make our login when we open the browser it automatically does the login.

How to implement optional authentication in React/Express app?

I'm building self-hosted bookmarks manager app. I'm using React on frontend and Express on backend. I have already implemented normal authentication system based on JWT. But now I want to make authentication optional. Beacuse it's self-hosted app some users might want to use it only on local network = auth disabled, and other expose it to the Internet = auth enabled.
On backend, I'm using env variable USE_AUTH=false to disable authentication and everything works as it should.
On frontend however, I'm rendering some components and routes based on isAuthenticated value stored in AuthContext state. It is set to true on successful login/sign up and to false on logout.
My question is how can I tell my React frontend that I'm not using auth on backend so it can render all hidden components, don't include token to requests etc.?
The only solution I came up with, is to send request when user opens the app to ask server if it's using authentication. So something like GET /api/auth/ping will return { useAuth: true } or { useAuth: false } depending on USE_AUTH value.
But I don't know if it is as optimal solution. Is there a better option / pattern I can use?
Thanks
I believe the correct way to do this is to specify in your react app, which routes are private and which ones are public, depending on that, components will be rendered.

Connecting Blazor Wasm to Identity Server 4 - Register and account management links not working

Hopefully someone can guide me in the right direction, because I've been working on this for a while now.
I've create a Blazor WASM hosted in .Net Core. However instead of using the identity in the Server (API) project, I wanted to use for authentication an Identity Server hosted in a different project (so that I can use a standalone Identity Server).
I've create an Identity Server 4 project and also scaffold-ed the Identity Razor pages (so that I have the full flow with registration, account management, password recovery etc.) instead of the basic 3 or so pages that Identity Server generates.
In my Blazor Client project I've added the following inside the Main method:
{
// Bind to the oidc section in the appsettings.
builder.Configuration.Bind("oidc", options.ProviderOptions);
options.UserOptions.RoleClaim = JwtClaimTypes.Role;
})
Also, in my appsettings file I have the following oidc section:
"oidc": {
"Authority": "https://localhost:5001",
"ClientId": "ProjectAllocation.Client",
"DefaultScopes": [
"openid",
"profile",
"roles",
"offline_access"
],
"PostLogoutRedirectUri": "/",
"ResponseType": "code"
}
}
In Identity Server, in the Startup ConfigureServices I am redirecting the Login \ Logout to use the pages in the scaffolded Identity Area:
var builder = services.AddIdentityServer(options =>
{
...
options.UserInteraction.LoginUrl = "/Identity/Account/Login";
options.UserInteraction.LogoutUrl = "/Identity/Account/Logout";
options.Authentication = new IdentityServer4.Configuration.AuthenticationOptions
{
CookieLifetime = TimeSpan.FromHours(10), // ID server cookie timeout set to 10 hours
CookieSlidingExpiration = true
};
})
Now the login and logout work and it seems I am getting the right token data in the client; I haven't implemented the API on the server side yet.
My problem is that the register and the account management links from the Client project are not working. If you would use the template from the VS with integrated identity server then you would be able to click on the register link inside the client app and be taken to the Account\Register page in the Identity area; also once you logged in you can click on the "Hello ...." link and be taken to the Account management in the Identity area.
However this doesn't work in my case. If I navigate from the browser directly to those areas then it works (i.e.: browse to https://localhost:5001/Identity/Account/Register: it works).
When I click on Register button in the Client app it just reloads the app with the following link:
https://localhost:44395/?returnUrl=%2Fauthentication%2Flogin : it looks as if the app is being asked to login, even though the Register page in the Identity Server is marked to allow anonymous access.
I am really puzzled as to why this doesn't work. I can't figure out which settings in the Blazor project sets the links to navigate to via the RemoteAuthenticatorView. I am considering replacing the register button so that it doesn't navigate via the RemoteAuthenticatorView anymore and instead it uses a regular link directly to the Identity Server Register page, but I am not sure what the implications are; also it's really annoying that I cannot get this to work properly.
I've even tried to change the path to the register page so that instead of Identity/Account/Register is Account/Register via the ConfigureServices in the Startup file in the Identity Server 4:
services.AddRazorPages(options => {
options.Conventions.AddAreaPageRoute("Identity", "/Account/Register", "Account/Register");
});
which works from the browser (https://localhost:5001/Account/Register), but it still didn't work from the WASM Blazor Client.
Any ideas?
Thanks,
Raz
I am leaving this here in case someone else stumbles upon this.
I've looked through the Blazor WASM code and for registration and account management it uses the NavigationManager to navigate to the paths supplied in RemoteRegisterPath and RemoteProfilePath properties of the AuthenticationPaths.
For some strange reason though it looks like you cannot navigate to an outside url: the NavigationManager will ignore the supplied base address and use the base address of the project. So even though I've tried to provide an address like https://localhost:5001/Identity/Account/Register, the application actually navigates to https://localhost:44395/Identity/Account/Register.
As a workaround I've created a controller called Account with two methods Register and Manage which will redirect to the address of the Identity Server. So the Blazor client will call the corresponding method in the Account controller in the Blazor API server project which will redirect to the corresponding Identity Server page.
I've modified the call to AddOidcAuthentication() in the Main method inside the Blazor Client project:
builder.Services.AddOidcAuthentication(options =>
{
// Bind to the oidc section in the appsettings.
builder.Configuration.Bind("oidc", options.ProviderOptions);
options.AuthenticationPaths.RemoteRegisterPath = "Account/Register";
options.AuthenticationPaths.RemoteProfilePath = "Account/Manage";
options.UserOptions.RoleClaim = JwtClaimTypes.Role;
})
I've also created an AccountController in the Controllers folder in the Blazor API server project:
[Route("[controller]")]
[ApiController]
public class AccountController : ControllerBase
{
[AllowAnonymous]
[HttpGet("Register")]
public IActionResult Register(string returnUrl)
{
return Redirect(#"https://localhost:5001/Identity/Account/Register?returnUrl=https://localhost:44395/" + returnUrl);
}
[AllowAnonymous]
[HttpGet("Manage")]
public IActionResult Manage(string returnUrl)
{
return Redirect(#"https://localhost:5001/Identity/Account/Manage?returnUrl=https://localhost:44395/" + returnUrl);
}
}
Might be a good idea to authorize the Manage method. Also the returnUrl would probably need some additional parameters (at least for the login \ logout there are more parameters).
In addition, I needed to make some small changes in the Identity files scaffolded in the Identity Server project to allow redirection to non-local paths.
There are certain things that can be improved and it does feel like a hack, but the solution works for now and I couldn't find any better alternatives.
The standard IdentityServer4 project templates does not include any pages for registering users, these page you have to develop your self inside IdentityServer4. To get user registration pages you can try one of these projects/products:
An ASP.NET Core IdentityServer4 Identity Template with Bootstrap 4 and Localization
AdminUI

Owin OpenIdConnect Active Directory HttpContext.GetOwinContext doesn't open microsoftonlin login page

I'm trying to use Owin and OpenIdConnect to authenticate users via active directory (office 365 online). I've followed this example and I managed to create a new MVC test project and get it all working. (Settings for AD app id, tenant, Web config etc all fine).
I'm now trying to add that functionality into my existing ASP.net mvc application and I can't get the dang thing to work.
This is what I have: An Account Controller with a "void" action like this (from the example that works in my PoC but not in my actual application):
public void SignIn()
{
// Send an OpenID Connect sign-in request.
if (!Request.IsAuthenticated)
{
HttpContext.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = "/" }, OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}
When this action is invoked, I expect the browser to be directed to: login.microsoftonline.com..., but instead it opens this page: https://localhost:44301/Account/Login?ReturnUrl=%2fAccount%2fSignIn
It's like it's calling some sort of redirect somewhere and I can't see where.
Help!
I found the answer. I had to do 2 things:
Remove the WebMatrix dll's from the references (apparently nuget package for mvc put it there, so it might come back)
Remove authentication mode="Forms" from web.config
Thanks.

Using an auth function on an onEnter route vs using a high order function and are both methods really secure?

Lets take a system where a jwt token is saved to local storage upon login and now we check for the token existence to allow a user access to a protected route.
We're using react-router-redux and bundling everything with webpack and our code is in the client/src folder as apposed to the server folder.
So the protected route with our auth check would look something like the following (let's say we send our routes only on demand) :
import { push } from 'react-router-redux'
function requireAuth(store) {
const check = localStorage.jwt
check ? store.dispatch(push('/dashboard')) : store.dispatch(push('/'))
}
export default (store) => ({
path: 'dashboard',
onEnter: requireAuth(store),
getComponent (nextState, cb) {
require.ensure([], (require) => {
const Dashboard = require('./containers/DashboardContainer').default
cb(null, Dashboard)
}, 'dashboard')
}
})
And for completion our index routes file:
import Login from './login'
import Dashboard from './Dashboard'
import Logout from './Logout'
export const createRoutes = (store) => ({
path: '/',
indexRoute: Login,
childRoutes: [
Dashboard(store), //Our protected route
Logout(store)
]
})
export default createRoutes
Putting a side man in the middle attacks and the inherint problems with the nature of javascript crypto methos as discussed here:
SPA best practices for authentication and session management
I would like to focus on the above illustrated method of a function placed on the onEnter to allow access:
If I understand correctly the above example is bundled and sent to the client, Now I might be wrong and the route definitions aren't actually being sent in the bundle.
So I guess the question is: does a user has access or can see our above route definitions by default - if so how do we prevent it?
Also in regards to our auth check in the example, which I've seen used and simply redirects if a token is not present in the local storage, is that really sufficient prevention?
Comparing the above example to using a high order function to wrap our protected component with, so we can preform auth checks in the life cycle methods WillMount and WillRecieveProps as you can see here for example.
I would think that both approaches would suffer from being exposed to the client. Am I right?
What other caveats am I missing regarding each approach?
In my opinion you shouldn't store you JWT token in localStorage, instead you should keep it as a cookie with httpOnly and secure flags as true. So that your client script can't get your JWT token. Also man in the middle attacks are not possible since you are sending your cookie on https only.
You could have a api endpoint which checks if the client has JWT and if it is valid. So that you can do your redirects on onEnter function wherever you need.
EDIT
If I understand correctly the above example is bundled and sent to the
client, Now I might be wrong and the route definitions aren't actually
being sent in the bundle. So I guess the question is: does a user has
access or can see our above route definitions by default - if so how
do we prevent it?
Yes your whole application is being sent to client with your all client code, so anyone can see what you have written if they want to.
Also in regards to our auth check in the example, which I've seen used
and simply redirects if a token is not present in the local storage,
is that really sufficient prevention?
Not really, if you have a invalid token, you redirect your user to page and if this page does some fetching from server, you will get 401 error and you will have to check 401 errors so that you can redirect user to login. Which doesn't seem efficient or clear.
Comparing the above example to using a high order function to wrap our
protected component with, so we can preform auth checks in the life
cycle methods WillMount and WillRecieveProps as you can see here for
example.
All of them are being exposed to client except httpOnly flag true cookie.
Hope it helps.

Resources