Category: programming

  • Authentication in Rust with Rocket

    Rust Lesson #637, Authentication of REST Services goes something like this. Set up a Rocket REST service to require login authentication.

    There will be three sets of REST endpoints:

    • login which will read the user’s credentials from the request header and write a session key to a private cookie,
    • the endpoints you want to protect, which will just be your normal Rocket-style endpoint decorated with a new argument of type that implements rocket::request::FromRequest,
    • logout which will scrub authentication information from your cookies.

    Login

    The first time a user accesses your service, the user should be directed to use a login post which will expect a rocket::serde::json::Json<> structure containing any information you use to setup a session, like username and password, and a reference to a rocket::http::CookieJar to place a session key into.

    #[post("/users/login", format = "application/json", data = "<login_info>")]
    pub fn login(login_info: Json<LoginInfo>, cookies: &CookieJar<'_>) -> Option<String> {
    	....
    }
    

    Rocket uses reflection and annotation to perform magic on your route definition to ensure that in order to be well-formed, a request must provide values for the parameters. If a request doesn’t satisfy the parameters at face value, Rocket will respond with a 404.

    Inside your route definition, you can check login_info provided in the request header and consult your database or oracle to see if user should be able to authenticate.

    Upon success, you can write a session key to be used in subsequent requests. Typically, you’ll hash or encrypt a value readable only by the server to be used as a session key that would include fields like a user id, session id, and expiration time. Here all you have to do is write out a clear text string, for example JSON or whitespace separated values. Rocket’s cookie framework will encrypt the cookie value (but not the key) using the secret key in the Rocket.toml configuration.

    let token = "user638 1642533881115".to_string();
    let cookie = Cookie::build(AUTH_COOKIE, token).finish();
    cookies.add_private(cookie);
    

    Rocket also delivers the cookie back to the client.

    Session authentication

    This next part is the tricky one, since Rocket performs some sneaky dependency injection. When you register a route with a parameter implementing the FromRequest trait, Rocket will instantiate an implementation and invoke the from_request() method to determine if access to the route should be granted (Success), denied (Failure), or forwarded to another route (Forward).

    The parameter to from_request() is a Request instance that can provide the cookie provided during login. Parse the cookie’s value and determine whether access to the REST request should proceed.

    You might not need to touch the FromRequest instance in your route. It’s presence just signals the compiler to wire up authentication provided in your FromRequest::from_request() implementation to the route. You can attach other functionality to the FromRequest, such as information gleaned from authentication, like the username.

    Logout

    This is the easiest step. You’ll just set up a GET request whose only parameter is a rocket::http::CookieJar reference, from which you will remove your session-authentication cookie.

    static AUTH_COOKIE: &str = "auth";
    
    #[get("/users/logout")]
    pub fn logout(cookies: &CookieJar<'_>) -> Option<String> {
        cookies.remove_private(Cookie::named(AUTH_COOKIE));
        Some("OK".to_string())
    }
    

    Debugging notes

    Use curl to verify that the REST service authenticates before hacking away at your front end.

    Before starting this lesson, I was less-familiar with attaching a JSON header to the request and using cookies. Headers are easy, just use the --data and --header CLI options.

    curl --request POST --data '{"username":"some-user-name", "password": "clear-text-secret"}' --header "Content-Type: application/json" 192.168.1.9:7999/users/login -c -
    

    Cookies are a little tricker, but only because there’ll likely be a lot of gobbledy gook– you’ll be passing back encrypted values to the client. In the above example, the -c flag to curl signals to write cookies to a file. The output file - signifies to write to standard out, but you can write to a file for ensuring that the login session is active and later read the cookie file with the -b flag in another request.

    curl -v -b cookies.txt 192.168.1.9:7999/action/get/10/0
    

    You can even use both -b and -c with the same value and curl will update the cookies file.

    On versioning

    As with any software project, it doesn’t take too long to travel to versioning-hell. The notes above refer to my experience with Rocket 0.5.0-rc.1 using the secrets, tls, and json features. The features required serde 1.0.130, serde_json 1.0.67, and rocket_contrib 0.4.10 dependencies. rocket_cors is also useful if you want to separate your front-end from REST requests.

  • React, Redux, and Typescript

    Talking with a react-redux user earlier this year, I thought I should probably get with the program and learn some sort of state-management framework. I’m posting my notes from getting started with Redux applied to React and Typescript, hopefully so others and my future self can quickly jump into the ECMAscript pot-pourri.

    Redux

    The react-redux primer provides a gentle introduction to a rather disjointed framework. Honestly, Redux looks like it was extracted from a webapp following good design patterns without much regard to usability on the framework for developers outside the original project.

    Redux prescribes a framework to

    • use “immutable” application state objects (there’s no enforcement of immutability)
    • invoke actions as pure functions to build a new state from a previous state
    • store and dispatch actions for agents/components/etc driving state changes and agents/components/subscribing to state changes to respond.

    The result compartmentalizes all of your application logic from presentation, making things easy to compose and test, though not necessarily to read. Much of the processing in Redux just building objects to represent state or dispatch and there’s no support from ECMAscript to flag usage errors, so it’s easy for users make mistakes wiring up components or violate “immutability”.

    To add to any confusion documentation overloads the word “action” to mean a token describing which action to take, a factory to create the token, and the actual processing action. Combined with React, the term “state” is overloaded, sometimes denoting application state driving Redux, and sometimes the changing qualities of a React component, driving rendering.

    Mix in Typescript

    With the far-flung objects and files, having some compiler help would seem appreciated. In fact, using Typescript detects a lot of wiring mistakes early, but with several costs, especially since the Redux-with-Typescript introduction is pretty thin.

    Experimentation

    My experience incrementally adding Typescript to a sample project illuminated several benign mistakes, mostly surrounding polluted types. It’s easy to conflate the React properties of simple React components and those wired for Redux, or just to omit the former and use exclusively the latter — don’t do it!

    The benign mistakes are infuriating, since an app made with create-react-app will initially render and run…. and then halt once the Typescript compiler finds an error. The prevalence of these sorts of errors might discourage one from experimenting or refactoring. On the up-side, at least the default set up for unit-tests with Jest is more lax than the app served by react-scripts start.

    Versioning

    React has gone through so many versions and patterns, it can be difficult to align your usage with documentation and examples… and type definitions! Especially with the onset of React render and useState hooks, and the lag in update to declaration files.

    Dealing with Dynamic Types

    Redux leverages dynamic typing in ECMAscript to compose state and reducers as well as to decorate/inject React components with actions. The result can be a nightmare of tangled union types for you and the Typescript compiler.

    Where the Rubber Meets the Road

    Redux builds React component properties with react-redux.connect() a curried function taking two parameters, usually called mapStateToProps and mapDispatchToProps, each functions as well.

    Components respond to state changes with mapStateToProps(), which translates the application state to component properties. At minimum, the function should pull out the relevant fields to build some of the property fields for the component. Make sure to forward along only fields relevant to the component, as the Typescript compiler will infer types and throw an error if the JSX instantiating the component is missing the field.

    The dual to mapStateToProps() is mapDispatchToProps(), which allows components to forward messages to the reducer for state updates. The property fields that the function builds are methods to which components wire DOM events.

    You build a regular/simple React component, which you then pass to connect() which is used by react-redux to build a factory to create connected React components. It’s useful to distinguish simple from connected component classes, especially the property types used by the two:

    • You will export the connected components for JSX use. The connected components will likely have simplified properties, probably (hopefully) silently inferred by the Typescript compiler.
    • The simple components have all the property fields, which you will explicitly define, many of them actions, used to wire up and respond to DOM events.

    Tersely, mapStateToProps() provides property fields used for rendering; mapDispatchToProps() provides property fields that are functions to drive state changes. The union of the two should fill out the simple component property type.

  • Garballed Attractors

    Waiting for a flight home, I stumbled across a Medium blog post on attractors. The example system seemed simple enough and I had some spare time coming up, so I thought I’d try to ape a demo.

    Below is my flawed reconstruction. It’s not an attractor, but similar to a spirograph.