What is the best way to manage a user session in a Google App Engine application? Ideally I'd like to keep my application stateless and not save any user related data in memory, however I'm also afraid to send user credentials of the network on every request (not to mention authenticating the user on every request would require a call to the Datastore which costs money).
I checked out google's OAuth 2.0 solution but from my understanding it helps if my application wants to connect to any of the google APIs and needs permission from the client to access his google account.
Is there a go to way for managing user session? The most common scenario is to know which user initiated this request without having to send the userId as a request parameter.
Please note that we are not using third party providers. The user registers himself to our page normally and has a custom account. I'm not looking for tools that help integrate authentication with third party services. Otherwise I'd be using google's OAuth 2.0 or similar API
You can Always implement Authenticator Interface.
public class MyAuthenticator implements Authenticator {
#Override
public User authenticate(HttpServletRequest request) {
HttpSession session = request.getSession(false);
//
return null;// if not authenticated, otherwise return User object.
}
}
// Endpoints class.
#Api(name = "example", authenticators = { MyAuthenticator.class })
public class MyEndpoints {
public Profile getProfile(User user) {
if (user == null) {
throw new UnauthorizedException("Authorization required");
}
return new Profile(user.getEmail(), "displayName");
}
// store this class somewhere in models
public class Profile {
private String email;
private String displayName;
public Profile(String email, String displayName) {
this.email = email;
this.displayName = displayName;
}
public String getEmail() {
return email;
}
public String getdisplayName() {
return displayName;
}
}
}
Use the HttpServletRequest object to implement classic session based login or use your own custom header. Well that depends on your case. Return null when not authenticated and return User object when authenticated. Also implement some kind of encryption on both sides(client and server), so as to stop someone having the session key to access your api.
Related
in application.properties I need to set the OAuth2 keys...
OAuth2AppClientId=AB............................AN
OAuth2AppClientSecret=br................................u8
OAuth2AppRedirectUri=http://localhost:8085/oauth2redirect
Initially I put the keys in "" quotes assuming they should be treated as a string but to get it working I had to remove them. Can someone explain what's happening with
OAuth2AppClientId=AB............................AN when I build the app
and how do I find out more about OAuth2AppClientId?
A Google search is probably the place to start here. Here's a great resource about what a Client ID and Client Secret are:
https://www.oauth.com/oauth2-servers/client-registration/client-id-secret/
I quote:
The client_id is a public identifier for apps.
The client_secret is a secret known only to the application and the authorization server.
Intuit also has a ton of documentation on OAuth2, and how to implement it. You should read it:
https://developer.intuit.com/app/developer/qbo/docs/develop/authentication-and-authorization/oauth-2.0
In summary, the Client ID is how Intuit identifies that it's your app trying to connect to QuickBooks. Nothing is "happening" to the string when you build/compile the app - it's just normal string. But when your app authenticates against QuickBooks Online, your app sends the Client ID to QuickBooks so that QuickBooks knows it's your app trying to authorize a connection to QuickBooks, and not some other app.
If you want to see how to code is loading this, it is only a property being used inside the application
OAuth2PlatformClientFactory
#Service
#PropertySource(value="classpath:/application.properties", ignoreResourceNotFound=true)
public class OAuth2PlatformClientFactory {
#Autowired
org.springframework.core.env.Environment env;
OAuth2PlatformClient client;
OAuth2Config oauth2Config;
#PostConstruct
public void init() {
// intitialize a single thread executor, this will ensure only one thread processes the queue
oauth2Config = new OAuth2Config.OAuth2ConfigBuilder(env.getProperty("OAuth2AppClientId"), env.getProperty("OAuth2AppClientSecret")) //set client id, secret
.callDiscoveryAPI(Environment.SANDBOX) // call discovery API to populate urls
.buildConfig();
client = new OAuth2PlatformClient(oauth2Config);
}
public OAuth2PlatformClient getOAuth2PlatformClient() {
return client;
}
public OAuth2Config getOAuth2Config() {
return oauth2Config;
}
public String getPropertyValue(String propertyName) {
return env.getProperty(propertyName);
}
}
https://github.com/IntuitDeveloper/OAuth2-JavaWithSDK/blob/master/src/main/java/com/intuit/developer/sampleapp/oauth2/client/OAuth2PlatformClientFactory.java
Been looking into the identity server 4 solution to compliment my ASP CORE api.
Using a SPA page on front end, does IdentityServer4 have the capability to manage restfull calls for login/logout/other?
Currently my solution works perfectly to redirect to and from the IdentityServer4 solution, but wondering if i can improve on UX by avoiding the redirects that occur on login/logout?
I've heard of PopUp and iFrame capability, but from research that opens up other risks.
(not sure if this question is for stackoverflow or software engineering stack, happy to move it)
You may do this by using the resource owner password grant type, where you could provide your own login screen and pass the information to IdentityServer.
In IdentityServer you would implement the IResourceOwnerPasswordValidator interface to validate the users.
In your Startup.ConfigureServices add the following.
Services.AddTransient<IResourceOwnerPasswordValidator, ResourceOwnerPasswordValidator>();
Here is a sample ResourceOwnerPasswordValidator class.
public class ResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator
{
private IUserManager _myUserManager { get; set; }
public ResourceOwnerPasswordValidator(IUserManager userManager)
{
_myUserManager = userManager;
}
public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
{
var user = await _myUserManager.Find(context.UserName, context.Password);
if (user != null)
{
context.Result = new GrantValidationResult(
subject: user.USER_ID,
authenticationMethod: "custom",
claims: await _myUserManager.GetClaimsAsync(user));
}
else
{
context.Result = new GrantValidationResult(
TokenRequestErrors.InvalidRequest,
errorDescription: "UserName or Password Incorrect.");
}
}
}
The IUserManager implements the logic to check the database to validate the user.
Then the SPA client would use the GrantTypes.ResourceOwnerPassword. Here is an example you could start with.
DISCLAIMER
This is not the recommended flow to use.
Although there's very similar post, it is unanswered.
My JavaFX app with WebSocket will
send user id、password to server
keep the session to let user do something like personal data management.
Learning from
Oracle WebSocket、
Tyrus 8.14 Client HTTP Authentication
I have:
#ClientEndPoint
public class loginEndPoint {
final ClientEndpointConfig cec = ClientEndpointConfig.Builder.create().build();
public static void main(String [] args) {
AuthConfig authConfig = AuthConfig.Builder.create().disableBasicAuth().build();
Credentials credentials = new Credentials("ws_user", "password");
client.getProperties().put(ClientProperties.AUTH_CONFIG, authConfig);
client.getProperties().put(ClientProperties.CREDENTIALS, credentials);
client.connectToServer(new Endpoint() {
#Override
public void onOpen(Session session, EndpointConfig config) {
try {
session.addMessageHandler((MessageHandler.Whole<String>) (String message) -> {
System.out.println("Received message: "+message);
messageLatch.countDown();
});
//let user do some data management
} catch (IOException e) {
System.out.println("Connect Fail.");
}
}
}, cec, new URI("ws://localhost/myApp/login"));
}
}
Is these code right to do the authentication? And where could I do the server side authentication on #ServerEndPoint?
#ServerEndpoint
public class loginServerEndPoint {
}
Thanks for help.
No, it is not necessary to use Tyrus as a server implementation.
On the server-side you should secure WebSocket in exactly the same way as you secure servlet or jsp in your servlet container, which can be slightly different from container to container (mapping users to roles).
Look at authentication example
Note that this example shows up how to make authenticated WebSocket request handshake with BASIC auth scheme, but your client code disables it explicitly, so probably you want to use DIGEST scheme.
I'm using an AuthenticationService derived from AuthenticationBase in the standard business application template for RIA Services and using Forms authentication. I have my own CustomPrincipal that is created by my security infrastructure that I assign to Thread.CurrentPrincipal, so that it can be used by other service calls:
protected override bool ValidateUser(string userName, string password)
{
try
{
using (LoginService service = new LoginService())
{
SessionInfo info = service.Login(userName, password);
Thread.CurrentPrincipal = info.User;
SessionCache.Instance.Save(info);
}
}
catch (Exception e)
{
return false;
}
}
I've discovered, however, when other (non-authentication) RIA services are called, the Thread.CurrentPrincipal is reset to a GenericPrincipal object, so upon initialization of the other services, I override the Initialize() method of the domain service and set Thread.CurrentPrincipal by looking up the login in a session cache:
public override void Initialize(DomainServiceContext context)
{
base.Initialize(context);
if (context.User.Identity.IsAuthenticated)
{
SessionInfo info = SessionCache.Instance.FindByUsername(context.User.Identity.Name);
Thread.CurrentPrincipal = info.User;
}
}
Right now this lookup is being done by Username, because it is easily accessible in the GenericPrincipal, but I'd prefer that I could do the lookup via a session token. Is there a method with RIA services to easily persist a session token on the server during the lifetime of the session? I know I could put this session token in a cookie in the browser to persist it, but it seems like there should be a simpler method to persist a session token across the lifetime of the session. Perhaps the cookie method is the best way to do this, but I just wanted to confirm.
My question is similar to this question. I hope I can provide some more detail and context to get it answered.
So here's some context: I have a simple in-house silverlight (ver 4) app with WCF Ria services that I'm building for our small support team. It uses authentication against a third-party vended database, but all other user information, e.g. FriendlyName and Roles (only 1 role per user) comes from our own database. I'm trying to keep this simple and don't want to implement custom membership and role providers.
I have few domain service operations that I want to restrict to certain roles, so I tried using the RequiresRole attribute like so:
[RequiresRole("Admin", "HelpDesk", "Billing" )]
public RisStudyInfo GetStudyInfo(string accession) {
return ris.GetStudyInfo(accession);
}
On the client side WebContext.Current.User.IsInRole("Admin") returns true, but I always get access denied when calling the service. The RequiresAuthentication attribute works as expected.
Below is the implementation of my AuthenticationService. The User class simply inherits from UserBase and adds the FriendlyName property. Any ideas what I'm doing wrong?
[EnableClientAccess]
public class AuthenticationService : AuthenticationBase<User> {
UserDataService userData = new UserDataService();
protected override bool ValidateUser(string userName, string password) {
var auth = new DatabaseAuthenticator();
return auth.Authenticate(userName, password);
}
protected override User GetAuthenticatedUser(IPrincipal principal) {
User user = null;
if (principal.Identity.IsAuthenticated) {
user = new User();
user.FriendlyName = userData.GetFriendlyName(principal.Identity.Name);
user.Name = principal.Identity.Name;
user.Roles = GetRolesFor(user.Name);
}
return user;
}
private IEnumerable<string> GetRolesFor(string username) {
IList<string> roles = new List<string>();
string role = userData.GetRolesFor(username);
if (role != null)
roles.Add(role);
return roles;
}
Figured it out. At least 2 things wrong. First clue found here. The second clue here
1.Turns out I really do need to write a custom role provider. Only need to implement GetRolesForUser though.
public override string[] GetRolesForUser(string username) {
return new string[] { _userService.GetRolesFor(username) };
}
2.Configure the custom role provider correctly in the web.config
<roleManager cacheRolesInCookie="true" enabled="true" defaultProvider="MyRoleProvider">
<providers>
<add name="MyRoleProvider" type="MyProject.Web.Providers.MyRoleProvider, MyProject.Web"/>
</providers>
</roleManager>
I solved this one by using the local credential store to cache credentials. Whenever a local cred check fails a foreign check occurs and the cache is populated/updated. This was a trivial override of the ValidateUser method. It does mean that stale passwords continue to work until the updated password is used (it will fail locally, pass remotely and trigger an update).
This approach meant that internally everything worked as per an out of the box configuration with no need for any other mods (apart from removing the local create-a-user links).