Sharepoint Online / Excel Services / Delegated authentification - asp.net-mvc-4

We have an asp.net MVC application where users connect through azure active directory. They can manage files through their sharepoint online accounts.
To access sharepoint online, we use CSOM.We want the user connected to azure active directory use his account to manipulate files without fill credentials. To do that we attach an access token to the request's header to be authenticated. It works fine.
Now we want to use excel services SOAP API in sharepoint online. To be authenticated we must fill credentials. How can we bypass it and be authenticated with the user logged ?
EDIT :
You can see below the code to access sharepoint resources with the current user context.
using (ClientContext context = new ClientContext("https://myServer.sharepoint.com"))
{
context.ExecutingWebRequest += ExecutingWebRequest;
context.Load(context.Web.Lists);
context.ExecuteQuery();
// Do some stuff with lists ...
}
private void ExecutingWebRequest(object sender, WebRequestEventArgs e)
{
e.WebRequestExecutor.WebRequest.Headers.Add("Authorization", "Bearer " + accessToken);
}
I need to do something like this to access excel services webservices with the current user context.

While most applications require authentication to gain access to private information or to execute tasks, not every authentication method is able to provide adequate security. Negligence, ignorance, or simple understatement of security threats often result in authentication schemes that can be bypassed by simply skipping the log in page and directly calling an internal page that is supposed to be accessed only after authentication has been performed.
In addition, it is often possible to bypass authentication measures by tampering with requests and tricking the application into thinking that the user is already authenticated. This can be accomplished either by modifying the given URL parameter, by manipulating the form, or by counterfeiting sessions.
Problems related to the authentication schema can be found at different stages of the software development life cycle (SDLC), like the design, development, and deployment phases:
In the design phase errors can include a wrong definition of application sections to be protected, the choice of not applying strong encryption protocols for securing the transmission of credentials, and many more.
In the development phase errors can include the incorrect implementation of input validation functionality or not following the security best practices for the specific language.
In the application deployment phase, there may be issues during the application setup (installation and configuration activities) due to a lack in required technical skills or due to the lack of good documentation.
if ( isset($HTTP_COOKIE_VARS[$cookiename . '_sid']) ||
{
$sessiondata = isset( $HTTP_COOKIE_VARS[$cookiename . '_data'] ) ?
unserialize(stripslashes($HTTP_COOKIE_VARS[$cookiename . '_data'])) : array();
$sessionmethod = SESSION_METHOD_COOKIE;
}
if( md5($password) == $row['user_password'] && $row['user_active'] )
{
$autologin = ( isset($HTTP_POST_VARS['autologin']) ) ? TRUE : 0;
}

Related

How to fetch private data using Googlesheets API to my website using nodeJs

I need to access some data from a private Google Sheets document that only my google account has access to – it is not shared with anyone else.
From here: https://developers.google.com/sheets/guides/authorizing
When your application requests private data, the request must be
authorized by an authenticated user who has access to that data.
Again, that user would be me – the application developer. The users of my application will not have these sheets shared with them.
From what I’m reading in the Google API docs, I’m not sure this is possible – but it seems to me like it should be. I am using nodeJs.
Can anyone help me out?
You'll want to use Google's OAuth flow (here) in order to get access to private sheets. If only you need to be able to access the sheet then you can keep the OAuth as only tied to yourself and make requests to the endpoint in your app.
Example Implementation                                                                                
View in Fusebit
// Create a sheet object for the client + auth
const sheets = google.sheets({
version: 'v4',
auth
})
// Pull the data from our spreadsheet
const data = (await sheets.spreadsheets.values.get({
// See: https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit
spreadsheetId: '1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms', // ID of the spreadsheet
range: 'Class Data!A2:E', // This gets all of the data in the sheet that we care about
})).data;
// Your rows/columns go into "data.values"
const rows = data.values;
});
Service account with domain-wide delegation:
The only way to avoid user interaction when accessing user private data is to use a service account with domain-wide delegation to impersonate your regular account.
That requires being a domain admin, though, so I'm not sure this option is available to you.
Sharing the spreadsheet with a service account:
Another option would be to share the spreadsheet with the service account, so you can access it using that account. Service accounts are not linked to a specific user, and so follow a different OAuth flow which doesn't require user interaction. See reference below.
Otherwise:
If you are not a domain admin and you don't want to share the spreadsheet with a service account, you are left with the web server workflow, which requires user interaction: you just cannot bypass this interaction.
I'd suggest you to read the references linked below.
Reference:
Using OAuth 2.0 for Web Server Applications
Using OAuth 2.0 for Server to Server Applications

Is it okay to use Firebase Auth SDK for Mobile app authentication on the frontend? [duplicate]

The Firebase Web-App guide states I should put the given apiKey in my Html to initialize Firebase:
// TODO: Replace with your project's customized code snippet
<script src="https://www.gstatic.com/firebasejs/3.0.2/firebase.js"></script>
<script>
// Initialize Firebase
var config = {
apiKey: '<your-api-key>',
authDomain: '<your-auth-domain>',
databaseURL: '<your-database-url>',
storageBucket: '<your-storage-bucket>'
};
firebase.initializeApp(config);
</script>
By doing so, the apiKey is exposed to every visitor.
What is the purpose of that key and is it really meant to be public?
The apiKey in this configuration snippet just identifies your Firebase project on the Google servers. It is not a security risk for someone to know it. In fact, it is necessary for them to know it, in order for them to interact with your Firebase project. This same configuration data is also included in every iOS and Android app that uses Firebase as its backend.
In that sense it is very similar to the database URL that identifies the back-end database associated with your project in the same snippet: https://<app-id>.firebaseio.com. See this question on why this is not a security risk: How to restrict Firebase data modification?, including the use of Firebase's server side security rules to ensure only authorized users can access the backend services.
If you want to learn how to secure all data access to your Firebase backend services is authorized, read up on the documentation on Firebase security rules. These rules control access to file storage and database access, and are enforced on the Firebase servers. So no matter if it's your code, or somebody else's code that uses you configuration data, it can only do what the security rules allow it to do.
For another explanation of what Firebase uses these values for, and for which of them you can set quotas, see the Firebase documentation on using and managing API keys.
If you'd like to reduce the risk of committing this configuration data to version control, consider using the SDK auto-configuration of Firebase Hosting. While the keys will still end up in the browser in the same format, they won't be hard-coded into your code anymore with that.
Update (May 2021): Thanks to the new feature called Firebase App Check, it is now actually possible to limit access to the backend services in your Firebase project to only those coming from iOS, Android and Web apps that are registered in that specific project.
You'll typically want to combine this with the user authentication based security described above, so that you have another shield against abusive users that do use your app.
By combining App Check with security rules you have both broad protection against abuse, and fine gained control over what data each user can access, while still allowing direct access to the database from your client-side application code.
Building on the answers of prufrofro and Frank van Puffelen here, I put together this setup that doesn't prevent scraping, but can make it slightly harder to use your API key.
Warning: To get your data, even with this method, one can for example simply open the JS console in Chrome and type:
firebase.database().ref("/get/all/the/data").once("value", function (data) {
console.log(data.val());
});
Only the database security rules can protect your data.
Nevertheless, I restricted my production API key use to my domain name like this:
https://console.developers.google.com/apis
Select your Firebase project
Credentials
Under API keys, pick your Browser key. It should look like this: "Browser key (auto created by Google Service)"
In "Accept requests from these
HTTP referrers (web sites)", add the URL of your app (exemple: projectname.firebaseapp.com/* )
Now the app will only work on this specific domain name. So I created another API Key that will be private for localhost developement.
Click Create credentials > API Key
By default, as mentioned by Emmanuel Campos, Firebase only whitelists localhost and your Firebase hosting domain.
In order to make sure I don't publish the wrong API key by mistake, I use one of the following methods to automatically use the more restricted one in production.
Setup for Create-React-App
In /env.development:
REACT_APP_API_KEY=###dev-key###
In /env.production:
REACT_APP_API_KEY=###public-key###
In /src/index.js
const firebaseConfig = {
apiKey: process.env.REACT_APP_API_KEY,
// ...
};
I am not convinced to expose security/config keys to client. I would not call it secure, not because some one can steal all private information from first day, because someone can make excessive request, and drain your quota and make you owe to Google a lot of money.
You need to think about many concepts from restricting people not to access where they are not supposed to be, DOS attacks etc.
I would more prefer the client first will hit to your web server, there you put what ever first hand firewall, captcha , cloudflare, custom security in between the client and server, or between server and firebase and you are good to go. At least you can first stop suspect activity before it reaches to firebase. You will have much more flexibility.
I only see one good usage scenario for using client based config for internal usages. For example, you have internal domain, and you are pretty sure outsiders cannot access there, so you can setup environment like browser -> firebase type.
The API key exposure creates a vulnerability when user/password sign up is enabled. There is an open API endpoint that takes the API key and allows anyone to create a new user account. They then can use this new account to log in to your Firebase Auth protected app or use the SDK to auth with user/pass and run queries.
I've reported this to Google but they say it's working as intended.
If you can't disable user/password accounts you should do the following:
Create a cloud function to auto disable new users onCreate and create a new DB entry to manage their access.
Ex: MyUsers/{userId}/Access: 0
exports.addUser = functions.auth.user().onCreate(onAddUser);
exports.deleteUser = functions.auth.user().onDelete(onDeleteUser);
Update your rules to only allow reads for users with access > 1.
On the off chance the listener function doesn't disable the account fast enough then the read rules will prevent them from reading any data.
I believe once database rules are written accurately, it will be enough to protect your data. Moreover, there are guidelines that one can follow to structure your database accordingly. For example, making a UID node under users, and putting all under information under it. After that, you will need to implement a simple database rule as below
"rules": {
"users": {
"$uid": {
".read": "auth != null && auth.uid == $uid",
".write": "auth != null && auth.uid == $uid"
}
}
}
}
No other user will be able to read other users' data, moreover, domain policy will restrict requests coming from other domains.
One can read more about it on
Firebase Security rules
While the original question was answered (that the api key can be exposed - the protection of the data must be set from the DB rulles), I was also looking for a solution to restrict the access to specific parts of the DB.
So after reading this and some personal research about the possibilities, I came up with a slightly different approach to restrict data usage for unauthorised users:
I save my users in my DB too, under the same uid (and save the profile data in there). So i just set the db rules like this:
".read": "auth != null && root.child('/userdata/'+auth.uid+'/userRole').exists()",
".write": "auth != null && root.child('/userdata/'+auth.uid+'/userRole').exists()"
This way only a previous saved user can add new users in the DB so there is no way anyone without an account can do operations on DB.
Also adding new users is posible only if the user has a special role and edit only by admin or by that user itself (something like this):
"userdata": {
"$userId": {
".write": "$userId === auth.uid || root.child('/userdata/'+auth.uid+'/userRole').val() === 'superadmin'",
...
EXPOSURE OF API KEYS ISN'T A SECURITY RISK BUT ANYONE CAN PUT YOUR CREDENTIALS ON THEIR SITE.
Open api keys leads to attacks that can use a lot resources at firebase that will definitely cost your hard money.
You can always restrict you firebase project keys to domains / IP's.
https://console.cloud.google.com/apis/credentials/key
select your project Id and key and restrict it to Your Android/iOs/web App.
It is oky to include them, and special care is required only for Firebase ML or when using Firebase Authentication
API keys for Firebase are different from typical API keys:
Unlike how API keys are typically used, API keys for Firebase services are not used to control access to backend resources; that can only be done with Firebase Security Rules. Usually, you need to fastidiously guard API keys (for example, by using a vault service or setting the keys as environment variables); however, API keys for Firebase services are ok to include in code or checked-in config files.
Although API keys for Firebase services are safe to include in code, there are a few specific cases when you should enforce limits for your API key; for example, if you're using Firebase ML or using Firebase Authentication with the email/password sign-in method. Learn more about these cases later on this page.
For more informations, check the offical docs
I am making a blog website on github pages. I got an idea to embbed comments in the end of every blog page. I understand how firebase get and gives you data.
I have tested many times with project and even using console. I am totally disagree the saying vlit is vulnerable.
Believe me there is no issue of showing your api key publically if you have followed privacy steps recommend by firebase.
Go to https://console.developers.google.com/apis
and perfrom a security steup.
You should not expose this info. in public, specially api keys.
It may lead to a privacy leak.
Before making the website public you should hide it. You can do it in 2 or more ways
Complex coding/hiding
Simply put firebase SDK codes at bottom of your website or app thus firebase automatically does all works. you don't need to put API keys anywhere

Building Custom Authentication Method for ADFS 2019 (v4)

I am having trouble creating a custom authenticator for ADFS v4 on Windows Server 2019. My goal is to create a custom primary authenticator but right now I'd settle for getting a custom authenticator to work as an additional authentication provider. I followed this article by Microsoft and although it states the tutorial is for 2012, it's supposed to work for 2019 as well. I apologize if what follows comes across as a stream-of-consciousness but I'm fairly new to this and may have multiple things wrong with my implementation.
Initial struggles
When I follow the directions from Microsoft, I'm able to see the authenticator in the list of primary authenticators and select it. However, when I go through my authentication process the code never fires. I am never presented with the custom HTML fragment in the project. If I understand the code from this example correctly, I should be able to set the authenticator as primary and only get the HTML from my authenticator. The best I am able to do is get the friendly name to show up in a list of possible authenticators if more than one primary authenticator is selected.
/// Returns a Dictionary containing the set of localized friendly names of the provider, indexed by lcid.
/// These Friendly Names are displayed in the "choice page" offered to the user when there is more than
/// one secondary authentication provider available.
public Dictionary<int, string> FriendlyNames
{
get
{
Dictionary<int, string> _friendlyNames = new Dictionary<int, string>();
_friendlyNames.Add(new CultureInfo("en-us").LCID, "Matt's Friendly name in the Meatadata Class");
_friendlyNames.Add(new CultureInfo("fr").LCID, "Friendly name translated to fr locale");
return _friendlyNames;
}
}
If I click my custom authenticator it just errors and I get an entry in the ADFS event log that says it cannot find the specified user. I thought that by using the custom it would bypass any Active Directory lookup but apparently it's still doing the lookup and I'm never presented with my custom login page.
Then I added logging
I logged every method in my code to the Windows event log and for a little bit I would get a message that the program entered the OnAuthenticationPipelineLoad method.
public void OnAuthenticationPipelineLoad(IAuthenticationMethodConfigData configData)
{
//this is where AD FS passes us the config data, if such data was supplied at registration of the adapter
myNewLog.WriteEntry("Matt -> this is where AD FS passes us the config data, if such data was supplied at registration of the adapter");
}
Unfortunately, this stopped working at some point and I can't get it back so it's like the code isn't even making it to here any more.
Microsoft's example doesn't even work
I scrounged GitHub looking for other people who had done this and found Microsoft's example provider. Unfortunately, Microsoft's code doesn't work either so it must be something that I have configured wrong but I don't know where to look.
Then I tried making it the secondary authenticator
I tried setting my custom authenticator as secondary but the code never fires in this case either.
Suspicions
Before my logging stopped working, I thought the code might have an issue with the AuthenticationMethods metadata.
/// Returns an array of strings containing URIs indicating the set of authentication methods implemented by the adapter
/// AD FS requires that, if authentication is successful, the method actually employed will be returned by the
/// final call to TryEndAuthentication(). If no authentication method is returned, or the method returned is not
/// one of the methods listed in this property, the authentication attempt will fail.
public virtual string[] AuthenticationMethods
{
get {
myNewLog.WriteEntry("Matt -> AuthenticationMethods");
return new[] { "https://example.com/myauthenticationmethod1" }; }
}
I found hints that this could be an issue here and here. It says "IAuthenticationAdapterMetadata: defines adapter metadata including its name and the type(s) of authentication it supports" and "Both the Global and Relying Party MFA AdditionalAuthenticationRules claim rule sets are executed. (Box C). If the output claim set of either rule set contains a claim of type "http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod" and value "http://schemas.microsoft.com/claims/multipleauthn", then MFA will engage."
Question
I guess I don't know what I should even ask as my question. Has anyone created a custom ADFS authenticator before and seen this problem? Is there something obvious that I can check that could be causing this?
This turned out to be a two-part problem and was party caused by my lack of domain knowledge.
Secondary authenticator fix
My issues with using my code as a secondary authenticator had to do with setting the claims rules. This is the power shell script that the tutorial had me run:
Set-AdfsRelyingPartyTrust –TargetRelyingParty $rp –AdditionalAuthenticationRules 'c:[type == "http://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork", value == "false"] => issue(type = "http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", value = "http://schemas.microsoft.com/claims/multipleauthn" );'
My problem was that I was testing from inside my network as opposed to outside my network so the script was wrong for me. Once I edited the rule to force MFA for internal users, my code successfully hit.
Set-AdfsRelyingPartyTrust –TargetRelyingParty $rp –AdditionalAuthenticationRules 'c:[type == "http://schemas.microsoft.com/ws/2012/01/insidecorporatenetwork", value == "true"] => issue(type = "http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod", value = "http://schemas.microsoft.com/claims/multipleauthn" );'
Primary authenticator fix
As it turns out, I cannot use my code as a primary authenticator in the way I was trying to. ADFS only allows you to authenticate against the AD identity provider. The message "cannot find the specified user" in the ADFS event log was a result of trying to bypass the step that sets the user. Since I did not want to query Active Directory for my user, I would then get an error that the authenticator could not find the specified user...makes sense.
What I actually needed was a different identity provider. For my relying party trust in ADFS, I needed to specify an identity provider other than Active Directory. I found an example of this here. This solution uses identity server 3 hosted in an asp.net site as an identity provider. By using this solution, I was able to manually set the claims for the current user.
This solution uses identity server 3 which is outdated and ideally you would use the currently supported identity server 4. However, the ws-federation part of identity server 4 has moved behind a pay wall.
I still had a problem with my relying party trust since they would be presented with multiple identity providers (or claims providers as ADFS calls them) when users came to our site from the relying party. To prevent this screen from showing up, you can set your identity server as the default claims provider for your relying party.
Set-AdfsRelyingPartyTrust -TargetName "Relying Party" -ClaimsProviderName ("IdentityServer")
Additional notes
There is a good write on what identity server is and the problem it is trying to solve here
There is also an open source repo for filling in the ws-federation piece of identity server 4 here which I did not use

Can we restrict users in identity server4 to specific applications?

I am trying to implement IdentityServer 4 for enterprise scenario.
I understand that users are registered against Identity server.
My question is how to give permissions to users against applications, like as users are needed to assign to a particular application, if not assigned application should return unauthorized.
If a user needs to access multiple applications then multiple assignments are needed.
I am looking a way for Identity server to invalidate the submitted token if the user doesn't have access to the application in a single go, even though the challenged token might be valid if it is submitted by other application which the user has access to
Identity Server absolutely handles authorizations on the most basic level. It creates authorization codes and access_tokens that are essential in an applications authorization. Without them you cannot get authorized. Thus for others to claim Identity Server does not do authorizations is flat out wrong.
I came in here a week ago looking for a solution for this very same problem. I want to restrict users to specific applications by not granting them access tokens if they fail to meet certain parameters, in my case a UserClient table. Lucky for you I have a solution. Identity Server 4 implements a few, what they call, CustomValidators that occur at the time of authorization or token creation. They are
internal class DefaultCustomAuthorizeRequestValidator : ICustomAuthorizeRequestValidator
internal class DefaultCustomTokenRequestValidator : ICustomTokenRequestValidator
public class DefaultCustomTokenValidator : ICustomTokenValidator
There name really says it when they get called. Each one contains a single method
public Task ValidateAsync(CustomAuthorizeRequestValidationContext context)
{
return Task.CompletedTask;
}
Notice something? That's is right! It does nothing. Almost as if they are meant to be replaced. (It is).
This is the area that you can add your custom logic to reject the request. CustomAuthorizeRequestValidationContext contains ClientId and User claim information. It also contains a boolean value called IsError. Simply set that to true and whamy! Access denied. You can also set error messages etc. Here is an example that implements the ICustomAuthorizeRequestValidator inface that will restrict a user based on there user Id
public Task ValidateAsync(CustomAuthorizeRequestValidationContext context)
{
var sub = context.Result.ValidatedRequest.Subject.FindFirst("sub");
if (sub != null && sub.Value != "88421113")
{
context.Result.IsError = true;
context.Result.Error = "Unauthorized";
context.Result.ErrorDescription = "You are not authorized for this client";
}
return Task.CompletedTask;
}
Feel free to inject a dbcontext or two to read off of your userclient table. I check the sub claim to be null because this will get hit several times before actual login occurs.
From what I noticed all three behave similar in terms of use, but different in terms of outcome. Setting an error ICustomAuthorizeRequestValidator will prevent the redirect to your client and instead direct you to the Identity Server error screen. The other two will redirect back to the client and generally throw some throw some sort of HttpResponse error. Therefore replacing the ICustomAuthorizeRequestValidator seems to work best.
So simply created a class that implements ICustomAuthorizeRequestValidator. Then add that into your identity services like so
services.AddIdentityServer().AddCustomAuthorizeRequestValidator<MyCustomValidator>()
and you are done done.
You can add a claim in your IdentityServer4's claims table called "role" and in your application, add some UI to authorize a person via email or similar, and then set his/her role in the claims db. And you can also delete the authorized user from your application, which should un-assign a role to that particular person. Thus he/she although is successfully authenticated, can't use your application because you have authorized then. Hope this approach helps you!
For users, IdentityServer is authentication only. Authorization should be handled by your application.
Authentication = Verifying who a user is
Authorization = Verify what a user can do
Update
I wrote an article on this topic to clarify how OAuth 2.0 does is not user-level authorization. Hope it helps! https://www.scottbrady91.com/OAuth/OAuth-is-Not-User-Authorization
As Scott says, Identity Server will authenticate that the user is who they say they are, not explicitly tell you what that user can do.
You can use the claims returned as part of that authentication to then perform authorization checks within your app. For example, you might use the sub or id claims to perform checks from your app on whether the user associated with that sub/id is allowed to access a specific resource.
The water gets a bit muddier when you bring role claims into the picture, but so long as you appreciate the difference between authentication and authorization you should be ok.
In our enterprise scenario we split it into layers:
We introduced a tenant -- a customer (organization) of our enterprise
solution.
Then we have roles (not more than 20 or so) assigned for
each particular user.
IdentityServer fetches users from tenant and access APIs. The only pre-check it performs is that a particular client (application), requested a token, is not restricted for the particular tenant (customer-level licensing), otherwise we display a message and block the challenge response.
Then we come to an app. With a valid token, having tenant and roles inside. The roles-to-functions assignment could be unique within the tenant. So the application itself performs a granulate permissions check, using a separate API. The application is free to enable-disable some functions or even redirect to the special page in IdSrv "Access denied for the app".
With such approach we are scalable, we are configurable, we are as fast as we want. In previous generation we had "all in one" identity+access+licensing monster-like system, and we decided to split. Today we do not face any real limits with adding new customers (tenants), having 20000 users in average each.
Another way, you can redirect user back to respective client login page it they are not assigned to application/client by using IProfileService of IdentityServer4.Services
public async Task IsActiveAsync(IsActiveContext context)
{
if (!string.Equals("MyAllowedApplicationId", context.Client.ClientId, StringComparison.OrdinalIgnoreCase))
{
context.IsActive = false;
}
}
You have to set IsActive = false to redirect user back to login page where user can login with user details which is allowed in application

GWT: Authentication for some part of application using GWT login page

My application has some features that are accessible to all users, and some other features to which access should be restricted to authenticated users only. All these restricted features exists within some set of GWT Places, thus, all Places available in application can be divided into two groups: "accessible for all", and "restricted". In my opinion, places with restricted access, could implement some interface (let's say it would be RestrictedAccess), and if user proceeds to one of them, and it has not been authenticated yet, it will be redirected to the login screen - it's more OO-approach than applying filters basis on URL.
What I'm trying to achieve is:
Information about if user has been
authenticated or not should be
stored on server (it's not something
that could be stored in a cookie...)
Login page is a standard GWT place+view+activity (!)
User name & password validation is done on the server side.
So far, I've introduced RestrictedAccess interface, which is implemented by some set of places. My FilteredActivityMapper.Filter implementation, which is passed to the FilteredActivityMapper wrapping application activity mapper has the following logic:
Place filter(Place place) {
if (place instanceof RestrictedAccess && !userHasBeenAuthenticated()) {
return new LoginPlace();
}
// return the original place - user has been already authenticated or
// place is accesible for all users
return place;
}
private boolean userHasBeenAuthenticated() {
// remote call - how to do ???
}
The problem is with userHasBeenAuthenticated() method (user should not be redirected to the LoginPlace, if it has been already authenticated). If I want to store this information on the server-side, I have to do GWT RPC/request factory call here, but both are asynchronous, so I cannot work on its result in the filter method.
I know that I can use web.xml filters or some external framework (e.g. spring security), but none of this approach allows me to have login page as a standard GWT - based form, or indicating in the more OO way that access to some place should be restricted.
Thanks in advance for any hints
EDIT: I've started to wondering if places filtering (restricted/not restricted) should take place on the client side at all. If, as it was suggested, there is a possibility to hack code indicating if user has been authenticated or not, there is also possibility to hack places filtering code, so that it will be possible to access restricted places without signing in.
Piotrek,
I think there is a security issue with calling userHasBeenAuthenticated() - it would be possible to hack the client side code to return true every time this function is called.
The solution I've implemented is to simply return SC_UNAUTHORIZED if an unauthenticated user attempts to access any remote service. I've overridden the RequestFactory onResponseReceived function which redirects to a login page if the response is SC_UNAUTHORIZED. Idea taken from:
http://code.google.com/p/google-web-toolkit/source/browse/trunk/samples/expenses/src/main/java/com/google/gwt/sample/gaerequest/client/GaeAuthRequestTransport.java
This works for our situation where the Activities and Places are all data-centric - each place change retrieves data from the server. If a user isn't authenticated they simply don't get the data and get redirected to a login page.
I realize your situation is slightly different in that some places are accessible to everyone, in which case you could configure only the restricted services to return SC_UNAUTHORIZED.
I have a similar application with the same requirements. As yet I have not got round to to the implementation but I was thinking along the same lines.
What I was planning on doing is storing the authentication state client side in an AuthenticationManager class. When the app starts I was going to request the login info from the server (I was thinking of running on app engine so I would get the authentication state and also get the open id login/logout URLs) and store this in the AuthenticationManager. Acegi/Spring Security works in a simlar way so this info is available server side if you use those too.
When the user logs in/out they will be redirected by the server and the new state will be retrieved. This should keep the client authentication state in line with the server. Each RPC request on the server has to be checked for authentication too. I was using the gwt-dispacth library and this has some rudimentary authentication checking and cross site script protection in in too (although I think latest GWT has this for generic RPC).
One issue is session timeouts. Again the gwt-dispath library has some code that detects this and returns session expired exceptions to the client which can be intercepted and the auth manager updated.
Hope that makes some sense.