Author: [EMAIL PROTECTED] Date: Mon Sep 1 16:25:13 2008 New Revision: 3599
Added: wiki/RpcAuth.wiki Log: Created wiki page through web user interface. Added: wiki/RpcAuth.wiki ============================================================================== --- (empty file) +++ wiki/RpcAuth.wiki Mon Sep 1 16:25:13 2008 @@ -0,0 +1,217 @@ +#summary A simple API for embedding authentication tokens into RPC payloads. +#labels Phase-Design,RPC + +=GWT RPC-Auth / XSRF API= + +==Goals== + * Provide a framework that integrates best-known practices for securing RPC requests from AJAX apps + * Simple integration with existing authorization backends / frameworks + * OAuth : http://code.google.com/apis/accounts/docs/OAuth.html + * Simple server-provided username/password style + * API style should act as an enhancement of existing GWT RPC API + * An auth-enabled RPC endpoint should be usable without an auth provider (for hosted-mode, testing) + +==Non-goals== + * Provide an implementation of an authorization backend + * Provide application-level or business-logic security + +=Overview= + +RPC-Auth will provide an API and design patterns to allow developers to ensure that the RPC endpoint is being accessed by a duly-authorized client. In this framework, the design will revolve around maintaining a session token that the client application will transmit as part of each future RPC request. + +{{{ +// The developer is free to extend the token, provided it remains a Serializable type +// Instances constructed on the client may not have as strong a UUID value +class AuthToken extends Serializable { + // The time at which the token was created + public final long getCreationTimeMillis() {...} + + // A unique identifier for the token object + public final String getUUID() {...} + + //final hashCode() and equals() also provided based on UUID + final int hashCode() { ... } + final boolean equals(Object o) { ... } +} + +// Implemented in addition to RemoteService by the RemoteServiceServlet +interface RequiresAuthorization<T extends AuthToken> { + + // Applied to individual methods to indicate that setAuthToken should + // not be called. Typically applied to a login() function. + public @interface NoAuthorization{} + + // Guaranteed to be called after every RPC request + void clearAuthToken(); + + // This method will be called immediately before any RPC functions and + // will be called with null if no token is present. + // The exception allows for an early-out. + void setAuthToken(T token, Method m) throws InvalidAuthTokenException; +} + +// Add additional method to ServiceDefTarget; each ServiceDefTarget will +// have its own default AuthCallback that just replays the last token +// it was given +interface ServiceDefTarget { + // Delegates to AuthCallback.getAuthToken(). + AuthToken getAuthToken(); + + // This intentionally lacks a getAuthCallback() + + // Optional, used for cross-proxy authorization setup + void setAuthCallback(AuthCallback callback); +} + +// Add additional methods to RemoteServiceServlet +class RemoteServiceServlet { + // May be called only during a request with an AuthToken to send back + void setAuthToken(AuthToken token); +} + +// The developer isn't required to implement this. We'll provide a default +// implementation that's just used to share an AuthToken between multiple +// RPC proxy objects. Different RPC security domains can be established by using +// distinct AuthCallback objects. +interface AuthCallback<T extends AuthToken> { + // Called by ServiceDefTarget to get the AuthToken to use + T getAuthToken(); + + // Called before the AsyncCallback's onFailure; the same exception + // appears there as well unless an alternate Throwable is thrown. + void onFailure(InvalidAuthTokenException e) throws Throwable; + + // Called before the AsyncCallback's onSuccess with the latest token value + void onSuccess(T token); +} +}}} + +Even though this document focuses mainly on the RemoteServiceServlet API, the behaviors described will be implemented via the existing RPC / RPCRequest utility classes, so these functions will be available to developers who are not using RemoteServiceServlet: + +{{{ +class RPCRequest { + public <T extends AuthToken> T getAuthToken(Class<T> desiredType) throws InvalidAuthTokenException; +} +}}} + +=Simple login scenario= + +Define the RemoteService (and associated async variant): + +{{{ +interface MyRemoteService extends RemoteService { + @NoAuthorization + void login(String username, String password); + + String restrictedMethod(); +} +}}} + +Define the servlet code: +{{{ +class MyRemoteServiceImpl extends RemoteServiceServlet implements MyRemoteService, RequiresAuthorization<AuthToken> { + private AuthToken authToken; + + void clearAuthToken() { + authToken = null; + } + + void login(String username, String password) { + if (username.equals(password)) { + AuthToken authToken = new AuthToken(); + // Register authToken.getUUID() in some kind of session-tracking database + setAuthToken(authToken); + } + } + + String restrictedMethod() { + return authToken.getUUID(); + } + + void setAuthToken(AuthToken t, Method m) throws InvalidAuthTokenException { + // Check that the session-tracking db has t.getUUID(), that it's not expired, etc. + authToken = t; + } +} +}}} + +Client code: +{{{ +MyServiceAsync svc = GWT.create(MyService.class); +svc.login("foo", "foo", someCallback); +// later on +svn.restrictedCallback(someCallback); +}}} + +After the initial call to login() succeeds, the remote proxy object will always send the last AuthToken that it has received to the server. + +=Custom AuthToken data= + +It will be convenient to associate additional data in the client or server with the AuthToken, without the need to maintain an additional Map or other tracking data. This design allows the user to extend the AuthToken class in any way that is desired, with the caveats that the class must remain serializable and that the additional data will add to the overhead of every RPC request. + +Define a custom subclass of AuthToken: +{{{ +// This must remain Serializable, after all +class MyAuthToken extends AuthToken { + private String customerId; + public getCustomerId() {...} + public MyAuthToken(String customerId) { + this.customerId = customerId; + } +} +}}} + +The RemoteServiceServlet definition is similar, but note the differences: + +{{{ +class MyRemoteServiceImpl extends RemoteServiceServlet implements MyRemoteService, RequiresAuthorization<MyAuthToken> { + private MyAuthToken authToken; + + void clearAuthToken() { + authToken = null; + } + + void login(String username, String password) { + if (username.equals(password)) { + String customerId = getCustomerIdForUsername(username); + MyAuthToken authToken = new MyAuthToken(customerId); + // Register authToken.getUUID() in some kind of session-tracking database + setAuthToken(authToken); + } + } + + String restrictedMethod() { + return authToken.getUUID(); + } + + void setAuthToken(MyAuthToken t, Method m) throws InvalidAuthTokenException { + // Check that the session-tracking db has t.getUUID(), that it's not expired, etc. + authToken = t; + } +} +}}} + +The underlying plumbing will ensure that the correct type of AuthToken is passed into the developer-provided code or throws an InvalidAuthTokenException if the wrong type of AuthToken has been provided in the payload. + +=Shared authentication domains= + +For sufficiently complicated apps, it may be the case that different RPC proxy objects will inhabit the same or different authorization realms. Because each proxy object delegates to its AuthCallback object to maintain the AuthToken that will be sent with the request, we can place multiple RPC proxy objects in the same security realm by sharing a common AuthCallback instance among them. Objects that must live in a disjoint security realm will necessarily have a different AuthCallback instance. + +{{{ +// DefaultAuthCallback is provided by GWT and is the built-in AuthCallback for remote proxy objects +AuthCallback realm1 = new DefaultAuthCallback(); + +MyServiceAsync svc1 = GWT.create(MyService.class); +MyServiceAsync svc2 = GWT.create(MyService.class); +MyOtherServiceAsync svc3 = GWT.create(MyOtherService.class); + +ServiceDefTarget t1 = (ServiceDefTarget)svc1; +ServiceDefTarget t2 = (ServiceDefTarget)svc2; +ServiceDefTarget t3 = (ServiceDefTarget)svc3; +t1.setAuthCallback(realm1); +t2.setAuthCallback(realm1); +t2.setServiceEntryPoint("/other/URL"); +t3.setAuthCallback(realm1); +}}} + +In this example, calling login() on any of the async services will allow any of the other services to pass the same authorization data to the server. If the server has a time-based expiration policy for sessions, using any of the RPC endpoints will be sufficient to keep the session alive. --~--~---------~--~----~------------~-------~--~----~ http://groups.google.com/group/Google-Web-Toolkit-Contributors -~----------~----~----~----~------~----~------~--~---