Question
Since wiki.js uses JWT, I would like to like to utilize the JWT token in my current application to accomplish a single sign-on (SSO) experience. If a user is logged into the application, then the user should not have to log in again when accessing the wiki. I have a Spring Boot (Java) backend and I am having trouble signing the JWT, wikijs gives me forbidden errors when accessing the admin page. I have been referencing server\models\users.j:refreshToken to see how the token is signed. I was thrown off because it looks like the token is signed with both a private cert and a passphrase.
Can you please give me guidance on how to sign my token?
Host Info
OS: [Windows]
Wiki.js version: [e.g. 2.0.0-beta.180]
Spring Version: 2.1.4.RELEASE (spring-boot-starter-parent)
Java Version: 8
With the help of @nicolasg-playster I was able to resolve this issue. I have attached an example jwt junit test as an example of how to implement this. I have used this implementation to successfully embed an iframe in my current application while having the current user already logged into the wiki when they navigate to the page containing the iframe.
Here is a snippet of how to get the decrypted private key from the pem cert created in setup.js. You can use that key to be able to sign a jwt that wikijs will be able to parse. Finally, you will obviously need to populate the claims in the jwt to match server\models\users.js. This token is stored as a cookie with the name jwt.
// this is where the magic happens...
// https://stackoverflow.com/questions/1774469/how-does-the-rsa-private-key-passphrase-work-under-the-hood
// https://github.com/bcgit/bc-java/blob/c0d5ac5e62a6531f640bdfb4897a9fe95a0c9fdb/pkix/src/test/java/org/bouncycastle/openssl/test/ParserTest.java#L412
public PrivateKey loadPrivateKey() throws IOException {
// pem key converter - used to convert an encrypted PEMKeyPair to a decrypted KeyPair
// BC = Bouncy Castle
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
// pem decryptor - needed to decrypt the PEMKeyPair from the pem file and needs to be built with the passcode used
// to create the private key
PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder().setProvider("BC").build(passcode.toCharArray());
// PEMParser - Class for parsing OpenSSL PEM encoded streams containingX509 certificates, PKCS8 encoded keys and PKCS7 objects.
PEMParser pr = openPEMResource(fileName);
Object o = pr.readObject();
if (!(o instanceof PEMKeyPair) && !(o instanceof PEMEncryptedKeyPair)) {
throw new RuntimeException("error reading private key from disk");
}
// https://stackoverflow.com/questions/3711754/why-java-security-nosuchproviderexception-no-such-provider-bc
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
// this is most likely a PEMEncryptedKeyPair
// 1. decrypt the pem file using the decrypting provider built with the private key passcode
// this will produce a PEMKeyPair which is a holder for a public and private key (these values are not longer encrypted)
// 2. convert org.bouncycastle.openssl.PEMKeyPair to a java.security.KeyPair
KeyPair kp = (o instanceof PEMEncryptedKeyPair) ?
converter.getKeyPair(((PEMEncryptedKeyPair)o).decryptKeyPair(decProv)) : converter.getKeyPair((PEMKeyPair)o);
// we now have a decrypted public and private key ~ woohoo!
return kp.getPrivate();
}
// helper for getting a stream of the pem file
private PEMParser openPEMResource(String fileName) throws FileNotFoundException{
// load file from disk
InputStreamReader inputStream = new InputStreamReader(new FileInputStream(fileName));
// read file
Reader fRd = new BufferedReader(inputStream);
// return a objected used to parse pem files
return new PEMParser(fRd);
}
For the full example please see:
wikijs-jwt-example.txt
openPEMResource can be modified to use a StringReader if you are reading the private cert from the wiki db.
private PEMParser openPEMResource(String privateCert){
Reader fRd = new StringReader(privateCert);
return new PEMParser(fRd);
}
@fourgates @NGPixel : newbie question - Is the pem file mentioned here the same one used to configure SSL in config.yml ?
ssl:
enabled: true
port: 3443
provider: custom
format: pem
key: path/to/key.pem
cert: path/to/cert.pem
passphrase: null
dhparam: null
i think in my case i used the same private key. i used the pfx format though (another ssl option)
@bwalsh They are not the same no. The key in your config.yml is for HTTPS purposes only. The key used to sign the JWT is stored in the database under the settings table.
Most helpful comment
@bwalsh They are not the same no. The key in your config.yml is for HTTPS purposes only. The key used to sign the JWT is stored in the database under the settings table.