27 people have subscribed since last month’s edition. If this is your first Masilotti Monthly, welcome to the family!
This month’s edition is all about mobile app authentication. I’ve spent the better part of the past few weeks learning about the best ways to secure a mobile app and the trade-offs that come with each strategy.
That said, I’m not a security expert! I’m sharing what I learned from my research. Did I frame this differently than you think about it? Do you have a better solution? I would love to learn more; don’t hesitate to send me an email.
First up is the simplest to implement: token-based authentication. This is where the server generates a single token that is shared with multiple clients when someone signs in. Signing in on your iPhone uses the same token as signing in on your iPad.
A common approach is to generate the token when a user record is created. Here’s some example Ruby code to ensure the token isn’t already in use.
class User < ApplicationRecord before_create :generate_auth_token def generate_auth_token loop do self.auth_token = SecureRandom.hex(10) break auth_token unless Users::User.find_by(auth_token: auth_token) end end end
A big downside to this approach is that you have to store the token in plain text in your database. Otherwise you won’t be able to “share” the token when the same user signs in on a different device.
The security issue here is that anyone with read access to your database can effectively impersonate any user on your site. All they need to do is copy-paste someone’s token and they have permanent access to their account!
OAuth service provider
On the other end of the spectrum, we have the OAuth service provider strategy. Ever “Signed in with Google” on a third-party site? Google is the service provider. We can adopt the same approach for trusted clients, like our iOS apps, by becoming a provider.
There’s a fair amount of work required for this one, but there are huge security benefits. Axel Kee has a great write up on how to do this in Ruby on Rails building on top of Devise and Doorkeeper.
Since we are in the world of OAuth, we can dispense access tokens that expire and renew with refresh tokens. This creates an additional layer of security — if an access token is compromised, it limits the amount of time an attacker can use it.
However, OAuth also has its own security risk. To ensure the client is trusted, we need to embed a secret key in the app. But it isn’t too difficult to extract this string by decompiling the code. Aaron Parecki has more on why OAuth API keys aren’t safe in mobile apps.
This risk can be mitigated with even more work. PKCE, or as Aaron likes to call it, is an “on the fly” client secret. Read more on PKCE and mobile apps on Okta’s website.
JSON Web Tokens (JWT)
If this were a fairy tale, JWT would be “just right.” They offer more security than a single, plain-text token but require less work (on both sides!) than OAuth. JWT is an open standard that defines a compact and self-contained way for securely transmitting information between parties as a JSON object.
- The server can generate multiple keys per user
- Each key is encrypted, not stored in plain text
- Tokens can (optionally) be refreshed
- No secret key on the client to manage
This also simplifies the flow for the client app. All it is responsible for is saving an access token to the Keychain and using it for each request. If you want the added layer of security you can expire the tokens and require the client to manually refresh them as needed.
Authentication and Turbo
Most of this research is coming from a series I’m writing on hybrid iOS apps with Turbo. Part 3, forms and authentication, is taking a bit longer than the first 2 parts because there’s a lot of hard decisions involved with authentication. I’m hoping that this month of research will make it easier to understand.
A quick favor
Enjoyed this month’s edition of The Masilotti Monthly? I would love for you to share it with a friend!