How does Google allow userService.getCurrentUser() to be called without having it passed HttpServletRequest request param? I am looking to extend UserService but it does not allow me to do that easily.
I have created my own authentication mechanism which overlaps UserService so that I can allow Google supported users and users who will login automatically. But I am interested in knowing how can I send the getCurrentUser when called from any function.
This is the current workflow
Request to secure resource -> Filter -> set current user somewhere but I am not sure WHERE so that I can call it from the rest of the application without passing httprequests..
in my code
GetCurrentUser() should return the user set in the filter. Note that I will not be passing the original HttpServletRequest
Thanks.
After few days of research, we can safely save the session info in the ThreadLocal. This is set in the servlet filter.
Static ThreadLocal variable in a WebApp - Are there any Security/Performance issues?
Since each servlet is executed in a single thread, we can safely call getCurrentUser from the code which then reads from the threadlocal.
Caveat though. Threads are recycled. So you can get a thread which has threadlocal set for a different user. You have to make sure that it is reset everytime.
Related
In our application, we authenticate users using AAD, but we authorize users using our own User and Role tables in our local SQL database.
With the old ADAL library, we were able to return users to our callback url, then from there we would grab the JWT token from the ADAL service, and send that token to our server. The server would then validate the token, decode it and grab the email address. We then used our SQL tables to return another JWT that contained the user's identity and all of their roles.
With MSAL, this still works if you use InteractionType.Popup. The response Observable from the loginPopup() method carries the AuthentiationResult, which has an idToken property and an accessToken property. You can easily grab the one you need and you're off to the races.
However, with InteractionType.Redirect we don't get the AuthenticationResult.
I have injected the msalService instance into the our callbackURL's component (called AuthCallbackComponent). I looked everywhere within the msalService for the tokens, but couldn't find them. I even looked in sessionStorage, where I've configured MSAL to cache the token. They are actually in there (under a couple of really funky keys), but not until later. Whether I use ngOnInit, ngAfterViewInit or ngAftercontentInit, the tokens are not there yet. It does work if I set a timeout of 1-2 seconds, but...no. You can never really rely on timeout delays being long enough for all of your users all the time.
We desire to use the Redirect workflow rather than the popup workflow, so it really would be ideal if we can just get the idToken from the MSAL instance.
I found this post here: Retrieve token using msal, which offers some possible solutions, but the suggestions aren't helpful. It talks about a custom MSAL Interceptor, but that seems wrong. That's typically the HTTP interceptor that adds your token to the headers of your service calls. It also says you can subscribe to the callback and "do something with the returned token", but assuming they mean the callback of the msalService.loginRedirect() method, well that is just wrong. It doesn't return anything at all.
Remember, that in the old ADAL library, this worked. Also it still works with InteractionType.Popup (which we can't use). I expect those tokens must be in the MSAL instance somewhere, or else there's a method we can override, a callback we can provide, etc.
Thanks for reading this longer post.
Buried deep within the 10K pages of Microsoft documntation on MSAL, I found this event reference:
https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-angular/docs/v2-docs/events.md#the-inprogress-observable
So I used their example code to come up with my solution, which was to inject the msalBroadcastService into my AuthCallbackComponent. There I subscribe to its msalSubject$, and when a LOGIN_SUCCESS event happens, I can get the accessToken and idToken from the event's payload. Here is some code:
ngOnInit(): void {
this.msalBroadcastService.msalSubject$.pipe(filter((msg: EventMessage) => msg.eventType === EventType.LOGIN_SUCCESS)).subscribe(msg => {
const idToken = (msg.payload as any).idToken;
// now I can call my service method, passing in the idToken
}
In some Azure Samples on GitHub like this one we have an example of using ADAL to access a protected Web API resource. The effort is protected by a try/catch looking for an AdalException
I'll summarize the code as thus:
try
{
//pseudo code... configure a client to use an access token..
var token = ADAL.AcquireTokenAsync...
var httpClient = new HttpClient(baseUri, token);
// use the token for querying some protected API
var result = //await HttpClient to return data...
return View(result);
}
catch (AdalException)
{
// do something important with the exception,
// e.g. return an error View w/login link
}
So, as I start to flesh out my MVC controllers to use ADAL-access_token'd requests, do I really want all this try/catch business in every controller?
Does it make sense to create an ActionFilter? This snippet was inspired by code I saw at this Azure Sample
public class AdalErrorAttribute : FilterAttribute, IExceptionFilter
{
void IExceptionFilter.OnException(ExceptionContext filterContext)
{
if(filterContext.Exception is AdalException)
{
if (filterContext.RequestContext.HttpContext.Request.QueryString["reauth"] == "True")
{
//
// Send an OpenID Connect sign-in request to get a new set of tokens.
// If the user still has a valid session with Azure AD, they will not be prompted for their credentials.
// The OpenID Connect middleware will return to this controller after the sign-in response has been handled.
//
filterContext.RequestContext.HttpContext.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties(),
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}
}
}
My Context:
I'm taking a rather homogenous set of scaffolded MVC controllers that were EntityFramework-centric at the time they were generated.. but now need to be re-tooled to access my Web API (via my new AutoRest client)
#vibronet made a good point - don't do that for your WebAPI endpoints. They're called with bearer auth, and shouldn't be automatically redirected to a sign-in process. Give back a 401 indicating the credentials are invalid and let it go.
But for an MVC app the user is using interactively, this is a reasonable idea to follow, subject to a couple of constraints.
Make sure your filter is very tight about what it matches. Ie. make sure it's only handling exceptions you can reasonably-certainly draw to an authentication issue (vs. being an access-denied issue). Perhaps a custom exception that gets thrown by your wrapping logic, or some extra conditions beyond it being an ADALException to make sure it's an issue solveable by logging in again.
Unless you really want every request have this handling, look at attaching it at the controller or action layer instead of globally.
Look out for potential "loop" issues, where you get an error, tell the user to log in again, get the error, make them log in again, etc. Maybe set some data in the session when triggering the login, or in the login process, or something like that. Better to give an error message than get the user's browser stuck in an infinite-redirect loop. (of course, this applies to the manually-handle-the-exception case too)
Hope that helps.
There are two issues with the approach.
the sample code you mention and the filter implementation are for web APPs, not web API. That's an important difference. Web APPs can rely on the browser to interact with the user for authentication; web APIs don't know what UX the client has (or even if there's any UX at all) hence the error behavior is radically different
a controller might call multiple APIs, requiring multiple token requests for different resources- which can fail or succeed independently, and connect to different providers. Different controllers might call different resources. The reaction to the error might vary. A filter such as the one you prototyped would only help with a very narrow set of cases.
Hence I would say that unless your controllers are all expected to perform very homogeneous logic, a filter such as the above would not give you the flexibility you need.
I'm building a web app using MEAN stack.
While building the REST API I see a lot of examples with the following endpoint
/api/contacts/:id
for GET, PUT and DELETE methods.
I did something different, I enabled session in the Express framework and now I can see the user document id (for mongoDB) under req.session.req.payload._id when I do a HTTP request which enables me to access the document.
That why I also don't need to expose the user document id in the URL when I do
a HTTP request.
My question is which method is better and safer to use?
Also, how can I get the user id in Angular to pass to the HTTP request in case I don't use sessions.
And a final one... I also use JWT as middle-ware before calling the function which updates the DB. This gives me some sense of security but wouldn't it be possible for a user with the correct token to do HTTP requests with different ids and by that getting, updating and deleting other users data?
This won't be possible with the current method I'm using (sessions)
When you need to pass the user ID to the endpoints then your client code will need to know that and it's the backend that needs to provide it somehow - either as part of the returned toked or usually as part of the response to a successful login request. The client can store it in a cookie or a local storage to use later.
It is not very practical, though, to have to provide the ID of the user in every route like:
/api/contacts/:id
When your users need to be able to access other users contacts then you may need to use the ID in the route and you may not want to have another set of routes for a situation when the user wants to manipulate his or her own contacts.
What I've seen sometimes done in practice in situations like this is to use a special value for the ID like "me" and have your backend translate all routes like this:
/api/contacts/me
to:
/api/contacts/:id
where :id is the ID of the user who made the request, taken from the session. This can be done with a middleware to substitute the value for all routes at once, and then your normal controllers could use it like it was provided in the route to begin with.
I'm using adaljs. Everything seems great...I can log in, log out, wonderful.
Now, after an hour...I load up my app again and! NOT so wonderful. I debug and I see that adalAuthenticationService.userInfo.isAuthenticated == false and adalAuthenticationService.profile == undefined.
What do I do when I get this? How do I recover?
When do I use these functions and for what?
acquireToken
clearCache
clearCacheForResource
getCachedToken
getResourceForEndpoint
getUser
logOut
logIn
logOutInProgress
Most importantly, WHY are these not explained in detail (or even in brief!) on the adaljs repository?
Let's turn this into a wiki about adaljs functions and properties. We all want to know what they do, what they are for, and how to use them.
Edit
In my app.js, I have this code for handling authentication:
if (adalAuthenticationService.userInfo.isAuthenticated && adalAuthenticationService.userInfo.profile) {
var great = "everything is awesome";
_ld.extend($scope.user,adalAuthenticationService.userInfo);
$scope.successFullyLoggedIn($scope.user);
} else if(!adalAuthenticationService.userInfo.isAuthenticated && !adalAuthenticationService.userInfo.profile) {
adalAuthenticationService.clearCache();
adalAuthenticationService.login();
} else {
adalAuthenticationService.clearCache();
adalAuthenticationService.logOut();
}
The tokens that adal.js gets from AAD expires after every one hour. If you are using angular wrapper then adal will be able to automatically renew the tokens as long as there is a valid user logged in. If you are not using angular wrapper, then application has to take the responsibility of renewing the tokens, that is where the apis will come handy.
I will try to explain what each one of them do:
acquireToken: This is one of the main methods. Takes 2 parameters: resourceId and callback. ResourceId is the app id of application on azure portal. Adal first looks into cache to check if there is token present already. If not, it sends a request to AAD to get a new token. It passes the token or the error to the callback, which has a signature like this: callback(err, token).
clearCache: delete all items in the browser storage that adal stored. Kind of a fresh start.
clearCacheForResource: I am assuming you are using experimental version because this api is not part of released 1.0.12 version. This basically deletes the cache entries for a specific resource id.
getCachedToken: Looks into the caceh and returns the token or null for the given resource.
getResourceForEndpoint: This is useful when using angular wrapper. This looks into the endpoints mapping that user provided at the time of initializing adal. The purpose of this method is to resolve a url into resourceId which can then be fed to acquireToken method.
getUser: current logged in user object or null.
logOut: logs out the user, clears cache and redirect the user to postlogoutredirecturi (if specified), otherwise redirects the user to redirecturi.
login: logs in the user, creates the user object (which can be accessed using getUser), saves the id token in the cache. It also calls the callback that you have on application level on config object when initializing adal.
logOutInProgress: Do you mean loginInProgress? That is just a flag to use internally to see if there is an active login in progress.
Unfortunately, we do not have api guides, that can help people understand our apis better. I will bring this to team's notice. Thanks.
Back to your question: you can use getUser() to get the user object. If it is null, then call logIn() method to log in the user. If user.userName is not null but user.isAuthenticated is false, this means, the token has expired, call acquireToken method and pass clientId of the application to renew the token. Also, you want to check adalAuthenticationService.usernInfo.profile instead of adalAuthenticationService.profile.
A scenario:
A user tries to login into the application, the credentials than passed to node to check if he exists, if so, some auth credential is placed to true, and the account of the user is than returned to the site, and with angular i have a service that does the following:
angular.module(myapp).service('accountService',function($http){
var account= null;
return {
authUser = function(credentials){
....
};
setaccount = function(accountObj){
account = accountObj;
...
}
}
Now it's ok if no refresh is going on, but when a refresh does happen, what is the best way to determine if a user is still logged in or not?
For example: i took a look into mean stack to see their approch, and what they do is like make the userObject exposed so i can do window.loggedinUser (That comes from a session storage of some kind?)
There are other examples i assume that deals with the run method doing something, but my question is this:
every time a user clicks refresh should the program do something in the run to determine if the user is still logged in or not (meaning dealing with server request in every refresh)?
What is the better solution to do so, a simple example will be appreciated....
For most AngularJS applications the user credentials are fetched from the server via an asynchronous HTTP request. This creates a timing problem of when AngularJS starts up and when the application knows if there is a user session.
You'll have to set the user flag to false at startup, and when the asynchronous call finishes you'll have to change it's state (if the user is logged in).
This becomes a problem if the current route is restricted to signed in users only.
The best recommended approach is to use route promises. Your accountService should provide a method that will return a promise that fetches the current user's session. If it already has it, then great use that one, otherwise it has to perform a HTTP request to get. Either way, the result should always be a promise.
This is how you should make the user session availible in your application. Via a promise on the route.