Skip to main content

SSO Auth (jwt)

Expected JWT format

Your Octopus SSO endpoint must return a compact JWS (JWT) signed with HS256 using your shared secret.

Header

{
"alg": "HS256",
"typ": "JWT"
}

Payload

{
"sub": "your-immutable-user-id", // REQUIRED: stable, never-changing user identifier
"exp": 1737993600 // STRONGLY RECOMMENDED: UNIX seconds; typically ~1 hour in the future
}

Notes

  • sub must be persistent and immutable (avoid emails or nicknames).
  • exp does not control Octopus session length; to end a session call octopus.disconnectUser().
  • Keep the token minimal; do not include PII.

(Illustrative, not a real token)

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiJ1c2VyXzEyMzQ1IiwiaWF0IjoxNzM3OTkwMDAwLCJleHAiOjE3Mzc5OTM2MDB9.
mJp9o0i7y8RZ7c8l5nY2oRWnG5m2m5w1vG0E8g1bqS4

Generate a signed JWT for SSO

Using the provided secret signing key, create an endpoint following a below example

Nodejs (Express)

dependencies:

npm install jsonwebtoken@9.0.2

index.js:

const express = require('express');
const jwt = require('jsonwebtoken');

const app = express();
const SECRET = 'your-secret-signing-key';//Usually fetched from a secret manager

app.get('/generateOctopusSsoToken', (req, res) => {
const loggedUserId = howeverYouFetchYourCurrentUserId(req)

const token = jwt.sign(
{ sub: loggedUserId }, SECRET, { expiresIn: '1h', algorithm: 'HS256' }
);
res.json({ token });
});

app.listen(3000, () => console.log('Server running on port 3000'));
Python (Flask)

requirement.txt:

PyJWT==2.10.1

app.py:

from flask import Flask, jsonify
import jwt
import datetime

app = Flask(__name__)
SECRET = 'your-secret-key'# Usually fetched from a secret manager

@app.route('/generateOctopusSsoToken', methods=['GET'])
def generate_token():
exp = datetime.datetime.utcnow() + datetime.timedelta(hours=1)
token = jwt.encode({'sub': 'yourUserId', 'exp': exp}, SECRET, algorithm='HS256')
return jsonify({'token': token})

if __name__ == '__main__':
app.run(port=3000)
Java (Spring)

pom.xml:

  <dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.12.6</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.12.6</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId> <!-- JSON codec; alternatively jjwt-gson, jjwt-orgjson -->
<version>0.12.6</version>
<scope>runtime</scope>
</dependency>

App.java:

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;

import java.security.Key;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}

@RestController
class TokenController {

private static final String SECRET = "your-secret-key";
private static final Key KEY = Keys.hmacShaKeyFor(SECRET.getBytes());

@GetMapping("/generateOctopusSsoToken")
public Map<String, String> genToken() {
Map<String, String> resp = new HashMap<>();
try {
String token = Jwts.builder()
.subject("yourUserId")
.expiration(new Date(System.currentTimeMillis() + 3_600_000)) // 1 h
.signWith(KEY)
.compact();
resp.put("token", token);
} catch (Exception e) {
resp.put("error", "Failed to generate token: " + e.getMessage());
}
return resp;
}
}

sub: has to be a persistent and immutable identifier for the user.

exp: the expiration does not determine when the user will be logged out from octopus. if you want to expire the octopus session you must call octopus.disconnectUser()

warning

Avoid using mutable values for the sub attribute like an email or nickname, as they can change over time. Instead, use your own user ID if it fits the immutability requirements, or generate a unique octopusId associated with the user to ensure long-term consistency and flexibility. If the same user logs in again on another device or after reinstalling your application, the same sub value must be used.