Skip to content

Commit a887903

Browse files
committed
Security and Authentication
1 parent 088513c commit a887903

File tree

2 files changed

+123
-38
lines changed

2 files changed

+123
-38
lines changed
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
---
2+
title: Authentication
3+
order: 150
4+
---
5+
6+
## Authenticating Your Users
7+
8+
Most apps will want to have some concept of who the user is so that they can use your app to connect securely with
9+
external services, such as your API or cloud service.
10+
11+
In a mobile app, you will always need to call an external service (external to the user's device) that acts as the
12+
source of truth about your users credentials. It could be a service you manage or a third-party service like WorkOS,
13+
Auth0 or Amazon Cognito.
14+
15+
Authenticating the user serves two purposes:
16+
17+
1. It allows you to prove who they are when they connect to your API.
18+
2. It gives you an opportunity to grant or deny access to certain features of your app based on the authenticated state
19+
of the user.
20+
21+
Whilst some user data may ne stored on the user's device for convenience, you should not rely on this data to
22+
authenticate the user. This is because **the data is outside of your control**. So you will not be using the typical
23+
Laravel authentication mechanisms to check for an authenticated user.
24+
25+
<aside>
26+
27+
If you wish to enable or disable parts of your app based on your user's account level or status, make sure you check
28+
their eligibility based on data from your server on a regular basis, caching it for as short a time as possible on
29+
their device.
30+
31+
</aside>
32+
33+
## Tokens FTW!
34+
35+
Most mobile apps opt for some form of "auth token" (e.g. a JWT or an expiring API key) that is generated by your auth
36+
service and stored securely on the user's device.
37+
38+
These tokens should only live for a short period, usually no more than a few days. It's useful to have a single-use
39+
"refresh token" that lives for a longer time (e.g. 30 days) also shared with your user when they have successfully
40+
authenticated. This can be exchanged for a new auth token when the user's current auth token has expired.
41+
42+
You should store both auth and refresh tokens in secure storage. **Checking for an auth token's existence to validate
43+
that the user is authenticated is _not sufficient_.** If the token has expired or been revoked, you should force the
44+
user to re-authenticate. The only way to know for certain is to exercise the token.
45+
46+
<aside>
47+
48+
#### Pro Tip!
49+
50+
Use `Native\Mobile\Facades\Network::status()` to determine if the device is connected to the internet before making API
51+
calls.
52+
53+
</aside>
54+
55+
### Laravel Sanctum
56+
57+
[Laravel Sanctum](https://laravel.com/docs/12.x/sanctum) is a very convenient and easy-to-use mechanism for generating
58+
auth tokens for your users. They simply provide their login credentials and if authenticated, receive a token. Using a
59+
simple login form, you can collect their username and password in your app and `POST` it securely to your auth service
60+
via an API call.
61+
62+
Note that, by default, Sanctum tokens don't expire.
63+
[You should enable token expiration](https://laravel.com/docs/12.x/sanctum#token-expiration) for increased
64+
security. You may only find out that a token has expired when your app attempts to use it unsuccessfully.
65+
66+
### OAuth
67+
68+
OAuth is a robust and battle-tested solution to the mobile app auth problem. If you're running
69+
[Laravel Passport](https://laravel.com/docs/12.x/passport) or your authentication service support OAuth, you should use
70+
it!
71+
72+
You will likely want to use an OAuth client library in your app to make interacting with your OAuth service easier.
73+
74+
When initiating the auth flow for the user, you should use the `Native\Mobile\Facades\Browser::auth()` API, as this is
75+
purpose-built for securely passing authorization codes back from the OAuth service to your app.
76+
77+
You should set your redirect URL to `nativephp://127.0.0.1/some/route`, where `some/route` is a route you've defined in
78+
your app's routes that will be able to handle the auth code.
79+
80+
Note that the scheme of the redirect URL in this case is **always** `nativephp://`. This has nothing to do with any
81+
custom deep link scheme you may have set for your app. It is only tied to the `Browser::auth()` session.
82+
83+
<aside>
84+
85+
Make sure you have good security around your auth service's authentication endpoint. As it will be accessed from many
86+
devices via an API, standard browser security such as CSRF protections will not be available to you.
87+
88+
Ensure you have appropriate rate limiting in place and even consider using an authentication key that you distribute
89+
with your apps. These steps will all help defend the endpoint against abuse.
90+
91+
</aside>

resources/views/docs/mobile/2/concepts/security.md

Lines changed: 32 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -39,66 +39,60 @@ The device's secure storage encrypts and decrypts data on the fly and that means
3939
critical things like API tokens, keeping your users and your systems safe.
4040

4141
This data is only accessible by your app and is persisted beyond the lifetime of your app, so it will still be available
42-
the next time your app is open.
42+
the next time your app is opened.
4343

44-
### Why not use the Laravel `Crypt` facade?
4544

46-
By default, the `Crypt` facade - and by extension the `encrypt` and `decrypt` helper functions - all rely on the
47-
`APP_KEY` value set in your `.env` file.
45+
<aside>
4846

49-
We _will_ use Laravel's underlying `Encryption` class, but you should avoid using these helpers directly.
47+
Secure Storage is only meant for small amounts of text data, usually no more than a few KBs. If you need to store
48+
larger amounts of data or files, you should store this in a database or as a file.
5049

51-
In the context of distributed apps, the `APP_KEY` is shipped _with_ your app and therefore isn't secure. Anyone who
52-
knows where to look for it will be able to find it. Then any data encrypted with it is no better off than if it was
53-
stored in plain text.
50+
</aside>
5451

55-
Also, it will be the same key for every user, and this presents a considerable risk.
52+
### When to use the Laravel `Crypt` facade
5653

57-
What you really want is a **unique key for each user**, and for that you really need to generate your encryption key
58-
once your app is installed on your user's device.
54+
When a user first opens your app, NativePHP generates a **unique `APP_KEY` just for their device** and stores it in the
55+
device's secure storage. This means each instance of your application has its own encryption key that is securely
56+
stored on the device.
5957

60-
You could do this and update the `.env` file, but it would still be stored in a way that an attacker may be able to
61-
exploit.
58+
NativePHP securely reads the `APP_KEY` from secure storage and makes it available to Laravel. So you can safely use the
59+
`Crypt` facade to encrypt and decrypt data!
6260

63-
A better approach is to generate a secure key the first time your app opens, place that key in Secure Storage, and
64-
then use that key to encrypt your other data before storage:
61+
<aside>
6562

66-
```php
67-
use Illuminate\Encryption\Encrypter;
68-
use Illuminate\Support\Facades\Storage;
69-
use Native\Mobile\Facades\SecureStorage;
63+
Make sure you do not leak the `APP_KEY` or decrypted data inadvertently through error tracking or debug logging tools.
7064

71-
function generateRandomKey()
72-
{
73-
return base64_encode(
74-
Encrypter::generateKey(config('app.cipher'))
75-
);
76-
}
65+
</aside>
7766

78-
$encryptionKey = SecureStorage::get('encryption_key');
67+
This is great for encrypting larger amounts of data that wouldn't easily fit in secure storage. You can encrypt values
68+
and store them in the file system or in the SQLite database, knowing that they are safe at rest:
7969

80-
if (! $encryptionKey) {
81-
SecureStorage::set('encryption_key', $encryptionKey = generateRandomKey());
82-
}
83-
84-
$mobileEncrypter = new Encrypter($encryptionKey);
70+
```php
71+
use Illuminate\Support\Facades\Crypt;
8572

86-
$encryptedContents = $mobileEncrypter->encrypt(
73+
$encryptedContents = Crypt::encryptString(
8774
$request->file('super_private_file')
8875
);
8976

90-
Storage::put('my_secure_file.pdf', $encryptedContents);
77+
Storage::put('my_secure_file', $encryptedContents);
9178
```
9279

9380
And then decrypt it later:
9481

9582
```php
96-
$decryptedContents = $mobileEncrypter->decrypt(
97-
Storage::get('my_secure_file.pdf')
83+
$decryptedContents = Crypt::decryptString(
84+
Storage::get('my_secure_file')
9885
);
9986
```
10087

101-
### Secure Storage vs Database/Files
88+
<aside>
89+
90+
Data encrypted with the `Crypt` facade should stay on the user's device with your app. Placing it encrypted anywhere
91+
else risks the chance that it will be unrecoverable. If the user loses their device or deletes your app,
92+
they will lose the encryption key and the data will be encrypted forever.
93+
94+
If you wish to share data, decrypt it first, transmit securely (e.g. over HTTPS) and re-encrypt it with a different key
95+
that is safely managed elsewhere.
96+
97+
</aside>
10298

103-
Secure Storage is only meant for small amounts of text data, usually no more than a few KBs. If you need to store
104-
larger amounts of data or files, you should store this in a database or as a file.

0 commit comments

Comments
 (0)