Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
89a65e4
make common
Nonary May 14, 2025
0e41cf7
Adds capability to generate tokens and use them against sunshine API
Nonary Jun 22, 2025
4724bf8
Merge branch 'LizardByte:master' into token_auth
Nonary Jun 22, 2025
34c18a4
Update token process to store in disk
Nonary Jun 22, 2025
c495fdc
Polioshing web UI
Nonary Jun 22, 2025
80a0e52
restore mains order
Nonary Jun 22, 2025
334a6c4
sonarcloud suggestions
Nonary Jun 22, 2025
fa782ce
more sonarcloud fixes
Nonary Jun 22, 2025
7a661a5
more sonarcloud suggestions
Nonary Jun 23, 2025
f4e535e
cleanup
Nonary Jun 23, 2025
e86ca93
more cleanup
Nonary Jun 23, 2025
a2158a0
reduce nesting in load_api_tokens (sonar cube)
Nonary Jun 23, 2025
a2825c2
seems like the only way to get sonarcube to shut up is to wrap it int…
Nonary Jun 23, 2025
205fb64
remove redundant return type on lambda
Nonary Jun 23, 2025
bc3d682
delete dead file, remnant from an old branch
Nonary Jun 23, 2025
1652f65
Add doxygen
Nonary Jun 23, 2025
af869b7
fix dark mode, refactor so fg.disable() is not necessary in helper me…
Nonary Jun 23, 2025
3920437
remove dead code
Nonary Jun 23, 2025
acc6494
sonarcube suggestions again
Nonary Jun 24, 2025
30e9247
make it testable
Nonary Jun 24, 2025
7670cea
add jest unit testing
Nonary Jun 24, 2025
8aee2af
apply linting
Nonary Jun 24, 2025
ef1a000
refactor unit tests to not touch privates
Nonary Jun 25, 2025
f4ba078
Merge branch 'master' into token_auth
Nonary Jun 25, 2025
e9f9e7f
sonarcube fixes
Nonary Jun 25, 2025
53779de
more polishing
Nonary Jun 25, 2025
b947bbc
Merge branch 'master' into token_auth
Nonary Jun 25, 2025
3a69020
Add token based login
Nonary Jun 25, 2025
532cc7c
Remove unused SessionToken struct from confighttp header
Nonary Jun 25, 2025
bbc0bef
Security fixes for token and session authentication
Nonary Jun 25, 2025
556a3b3
remove session management from frontend and add logout capability
Nonary Jun 26, 2025
4a16efe
fix edge case where cookies could have bad characters
Nonary Jun 26, 2025
9f29573
extend sessions to 2 hours
Nonary Jun 26, 2025
775e2ef
cleanup comments in header file
Nonary Jun 26, 2025
5addae1
sonarcube fixes
Nonary Jun 26, 2025
c4dc857
fix test build errors
Nonary Jun 26, 2025
4c30938
more sonarcube fixes
Nonary Jun 26, 2025
d65edab
cleanup documentation
Nonary Jun 26, 2025
1912104
comment cleanup
Nonary Jun 26, 2025
0e0231b
cleanup
Nonary Jun 26, 2025
f561710
Remove unit tests for frontend as guided by RA
Nonary Jun 26, 2025
ba5563b
Merge branch 'master' into token_auth
Nonary Jun 26, 2025
7852876
add test scenarios discovered during mutation testing
Nonary Jun 27, 2025
11b82bc
Merge branch 'master' into token_auth
Nonary Jun 27, 2025
ce431c8
cleanup frontend comments and wire up the locale
Nonary Jun 27, 2025
80281cd
cleanup localization
Nonary Jul 4, 2025
e72abdb
Merge branch 'master' into token_auth
Nonary Jul 4, 2025
95fd680
Improve ux, add better auth handler and redirects
Nonary Aug 8, 2025
73c58aa
Merge branch 'master' into token_auth
Nonary Aug 8, 2025
280b13f
cleanup dead code add session timeout config option
Nonary Aug 8, 2025
64a7660
remove pointless comments
Nonary Aug 8, 2025
295a232
move doxygen
Nonary Aug 8, 2025
20b0660
improve consistency of code styling
Nonary Aug 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cmake/compile_definitions/common.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ set(SUNSHINE_TARGET_FILES
"${CMAKE_SOURCE_DIR}/src/stat_trackers.cpp"
"${CMAKE_SOURCE_DIR}/src/rswrapper.h"
"${CMAKE_SOURCE_DIR}/src/rswrapper.c"
"${CMAKE_SOURCE_DIR}/src/http_auth.cpp"
${PLATFORM_TARGET_FILES})

if(NOT SUNSHINE_ASSETS_DIR_DEF)
Expand Down
61 changes: 59 additions & 2 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,29 @@

Sunshine has a RESTful API which can be used to interact with the service.

Unless otherwise specified, authentication is required for all API calls. You can authenticate using
basic authentication with the admin username and password.
Unless otherwise specified, authentication is required for all API calls. You can authenticate using either basic authentication with the admin username and password, or with an API token that provides fine-grained access control.

@htmlonly
<script src="api.js"></script>
@endhtmlonly

## API Token Security Model and Best Practices

Sunshine API tokens are designed for security and fine-grained access control:

- **Token Creation:** When you generate an API token, Sunshine creates a secure random 32-character string. Only a cryptographic hash of the token is stored on disk and in memory. The raw token is shown to you only once—immediately after creation. If you lose it, you must generate a new token.
- **Security:** Because only the hash is stored, even if the state file is compromised, attackers cannot recover the original token value.
- **Principle of Least Privilege:** When creating a token, always grant access only to the specific API paths and HTTP methods required for your use case. Avoid giving broad or unnecessary permissions.
- **Token Management:**
- Store your token securely after creation. Never share or log it.
- Revoke tokens immediately if they are no longer needed or if you suspect they are compromised.
- Use HTTPS to protect tokens in transit.
- **Listing and Revocation:** You can list all active tokens (metadata only, not the token value) and revoke any token at any time using the API.

API Tokens can also be managed in the Web UI under the "API Token" tab in the navigation bar.

See below for details on token endpoints and usage examples.

## GET /api/apps
@copydoc confighttp::getApps()

Expand Down Expand Up @@ -57,6 +73,47 @@ basic authentication with the admin username and password.
## POST /api/restart
@copydoc confighttp::restart()

## Authentication

All API calls require authentication. You can use either:
- **Basic Authentication**: Use your admin username and password.
- **API Token (recommended for automation)**: Use a generated token with fine-grained access control.

### Generating an API Token

**POST /api/token**

Authenticate with Basic Auth. The request body should specify the allowed API paths and HTTP methods for the token:

```json
{
"scopes": [
{ "path": "/api/apps", "methods": ["GET", "POST"] },
{ "path": "/api/logs", "methods": ["GET"] }
]
}
```

**Response:**
```json
{ "token": "...your-new-token..." }
```
> The token is only shown once. Store it securely.

### Using an API Token

Send the token in the `Authorization` header:
```
Authorization: Bearer <token>
```

The token grants access only to the specified paths and HTTP methods.

### Managing API Tokens

- **List tokens:** `GET /api/tokens` (shows metadata, not token values)
- **Revoke token:** `DELETE /api/token/{hash}`

<div class="section_buttons">

| Previous | Next |
Expand Down
32 changes: 31 additions & 1 deletion docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -1685,6 +1685,36 @@ editing the `conf` file in a text editor. Use the examples as reference.
</tr>
</table>

### session_token_ttl_seconds

<table>
<tr>
<td>Description</td>
<td colspan="2">
Web UI session timeout in seconds. Determines how long a login session remains valid before re-authentication is required.
</td>
</tr>
<tr>
<td>Default</td>
<td colspan="2">@code{}
7200
@endcode (2 hours)
</td>
</tr>
<tr>
<td>Notes</td>
<td colspan="2">
For higher security on shared systems, reduce this value (e.g. 3600 for 1 hour). Minimum is 1 second.
</td>
</tr>
<tr>
<td>Example</td>
<td colspan="2">@code{}
session_token_ttl_seconds = 3600
@endcode</td>
</tr>
</table>

### log_path

<table>
Expand Down Expand Up @@ -2324,7 +2354,7 @@ editing the `conf` file in a text editor. Use the examples as reference.
</td>
</tr>
<tr>
<td>Default</td>
<td>Default</td>
<td colspan="2">@code{}
medium
@endcode</td>
Expand Down
10 changes: 10 additions & 0 deletions src/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,7 @@ namespace config {
platf::appdata().string() + "/sunshine.log", // log file
false, // notify_pre_releases
{}, // prep commands
std::chrono::hours {2} // session_token_ttl default 2h
};

bool endline(char ch) {
Expand Down Expand Up @@ -1298,6 +1299,15 @@ namespace config {
vars.erase(it);
}

// Parse session token TTL (seconds) if provided
{
int ttl_secs = -1;
int_between_f(vars, "session_token_ttl_seconds", ttl_secs, {1, std::numeric_limits<int>::max()});
if (ttl_secs > 0) {
sunshine.session_token_ttl = std::chrono::seconds {ttl_secs};
}
}

if (sunshine.min_log_level <= 3) {
for (auto &[var, _] : vars) {
std::cout << "Warning: Unrecognized configurable option ["sv << var << ']' << std::endl;
Expand Down
1 change: 1 addition & 0 deletions src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ namespace config {
std::string log_file;
bool notify_pre_releases;
std::vector<prep_cmd_t> prep_cmds;
std::chrono::seconds session_token_ttl; ///< Session token time-to-live (seconds)
};

extern video_t video;
Expand Down
Loading