[Libreoffice-commits] online.git: loolwsd/Auth.hpp

Pranav Kant pranavk at collabora.com
Mon Mar 21 13:56:56 UTC 2016


 loolwsd/Auth.hpp |  159 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 156 insertions(+), 3 deletions(-)

New commits:
commit 14823f29b43c7dd3f7d378c2bea562ee0e791ba4
Author: Pranav Kant <pranavk at collabora.com>
Date:   Mon Mar 21 13:58:05 2016 +0530

    loolwsd: JWT Auth - Generation and verification of JWT tokens
    
    Change-Id: If97fa52866f8d1d8976bd1a7340e2dac07e56c03

diff --git a/loolwsd/Auth.hpp b/loolwsd/Auth.hpp
index c454e3b..ce7ee91 100644
--- a/loolwsd/Auth.hpp
+++ b/loolwsd/Auth.hpp
@@ -14,9 +14,18 @@
 #include <cstdlib>
 #include <string>
 
+#include <Poco/Base64Encoder.h>
+#include <Poco/Base64Decoder.h>
+#include <Poco/Crypto/RSADigestEngine.h>
+#include <Poco/Crypto/RSAKey.h>
+#include <Poco/DigestEngine.h>
+#include <Poco/JSON/Object.h>
+#include <Poco/LineEndingConverter.h>
 #include <Poco/Net/HTTPClientSession.h>
 #include <Poco/Net/HTTPRequest.h>
 #include <Poco/Net/HTTPResponse.h>
+#include <Poco/StringTokenizer.h>
+#include <Poco/Timestamp.h>
 #include <Poco/URI.h>
 
 #include "Util.hpp"
@@ -27,12 +36,156 @@ class AuthBase
 public:
 
     /// Called to acquire an access token.
-    virtual bool getAccessToken() = 0;
+    virtual const std::string getAccessToken() = 0;
 
     /// Used to verify the validity of an access token.
     virtual bool verify(const std::string& token) = 0;
 };
 
+class JWTAuth: public AuthBase
+{
+public:
+    JWTAuth(const std::string keyPath, const std::string name, const std::string sub, const std::string aud)
+        : _name(name),
+          _sub(sub),
+          _aud(aud),
+          _key(Poco::Crypto::RSAKey("", keyPath)),
+          _digestEngine(_key, "SHA256")
+    {    }
+
+    const std::string getAccessToken()
+    {
+        std::string encodedHeader = createHeader();
+        std::string encodedPayload = createPayload();
+
+        // trim '=' from end of encoded header
+        encodedHeader.erase(std::find_if(encodedHeader.rbegin(), encodedHeader.rend(),
+                                         [](char& ch)->bool {return ch != '='; }).base(), encodedHeader.end());
+        // trim '=' from end of encoded payload
+        encodedPayload.erase(std::find_if(encodedPayload.rbegin(), encodedPayload.rend(),
+                                          [](char& ch)->bool { return ch != '='; }).base(), encodedPayload.end());
+        Log::info("Encoded JWT header: " + encodedHeader);
+        Log::info("Encoded JWT payload: " + encodedPayload);
+
+        // Convert to a URL and filename safe variant:
+        // Replace '+' with '-' && '/' with '_'
+        std::replace(encodedHeader.begin(), encodedHeader.end(), '+','-');
+        std::replace(encodedHeader.begin(), encodedHeader.end(), '/','_');
+
+        std::replace(encodedPayload.begin(), encodedPayload.end(), '+','-');
+        std::replace(encodedPayload.begin(), encodedPayload.end(), '/','_');
+
+        std::string encodedBody = encodedHeader  + "." +  encodedPayload;
+
+        // sign the encoded body
+        _digestEngine.update(encodedBody.c_str(), static_cast<unsigned>(encodedBody.length()));
+        Poco::Crypto::DigestEngine::Digest digest = _digestEngine.signature();
+
+        // The signature generated contains CRLF line endings.
+        // Use a line ending converter to remove these CRLF
+        std::ostringstream ostr;
+        Poco::OutputLineEndingConverter lineEndingConv(ostr, "");
+        Poco::Base64Encoder encoder(lineEndingConv);
+        encoder << std::string(digest.begin(), digest.end());
+        encoder.close();
+        std::string encodedSig = ostr.str();
+
+        // trim '=' from end of encoded signature
+        encodedSig.erase(std::find_if(encodedSig.rbegin(), encodedSig.rend(),
+                                      [](char& ch)->bool { return ch != '='; }).base(), encodedSig.end());
+
+        // Be URL and filename safe
+        std::replace(encodedSig.begin(), encodedSig.end(), '+','-');
+        std::replace(encodedSig.begin(), encodedSig.end(), '/','_');
+
+        Log::info("Sig generated is : " + encodedSig);
+
+        const std::string jwtToken = encodedBody + "." + encodedSig;
+        Log::info("JWT token generated: " + jwtToken);
+
+        return jwtToken;
+    }
+
+    bool verify(const std::string& accessToken)
+    {
+        Poco::StringTokenizer tokens(accessToken, ".", Poco::StringTokenizer::TOK_IGNORE_EMPTY | Poco::StringTokenizer::TOK_TRIM);
+
+        std::string encodedBody = tokens[0] + "." + tokens[1];
+        _digestEngine.update(encodedBody.c_str(), static_cast<unsigned>(encodedBody.length()));
+        Poco::Crypto::DigestEngine::Digest digest = _digestEngine.signature();
+
+        std::ostringstream ostr;
+        Poco::OutputLineEndingConverter lineEndingConv(ostr, "");
+        Poco::Base64Encoder encoder(lineEndingConv);
+
+        encoder << std::string(digest.begin(), digest.end());
+        encoder.close();
+        std::string encodedSig = ostr.str();
+
+        // trim '=' from end of encoded signature.
+        encodedSig.erase(std::find_if(encodedSig.rbegin(), encodedSig.rend(),
+                                      [](char& ch)->bool { return ch != '='; }).base(), encodedSig.end());
+
+        // Make the encoded sig URL and filename safe
+        std::replace(encodedSig.begin(), encodedSig.end(), '+', '-');
+        std::replace(encodedSig.begin(), encodedSig.end(), '/', '_');
+
+        if (encodedSig != tokens[2])
+        {
+            Log::info("JWTAuth::Token verification failed; Expected: " + encodedSig + ", Received: " + tokens[2]);
+            return false;
+        }
+
+        return true;
+    }
+
+private:
+    const std::string createHeader()
+    {
+        // TODO: Some sane code to represent JSON objects
+        std::string header = "{\"alg\":\""+_alg+"\",\"typ\":\""+_typ+"\"}";
+
+        Log::info("JWT Header: " + header);
+        std::ostringstream ostr;
+        Poco::OutputLineEndingConverter lineEndingConv(ostr, "");
+        Poco::Base64Encoder encoder(lineEndingConv);
+        encoder << header;
+        encoder.close();
+
+        return ostr.str();
+    }
+
+    const std::string createPayload()
+    {
+        std::time_t curtime = Poco::Timestamp().epochTime();
+        std::string exptime = std::to_string(curtime + 3600);
+
+        // TODO: Some sane code to represent JSON objects
+        std::string payload = "{\"iss\":\""+_iss+"\",\"sub\":\""+_sub+"\",\"aud\":\""+_aud+"\",\"nme\":\""+_name+"\",\"exp\":\""+exptime+"\"}";
+
+        Log::info("JWT Payload: " + payload);
+        std::ostringstream ostr;
+        Poco::OutputLineEndingConverter lineEndingConv(ostr, "");
+        Poco::Base64Encoder encoder(lineEndingConv);
+        encoder << payload;
+        encoder.close();
+
+        return ostr.str();
+    }
+
+private:
+    const std::string _alg = "RS256";
+    const std::string _typ = "JWT";
+
+    const std::string _iss = "lool";
+    const std::string _name;
+    const std::string _sub;
+    const std::string _aud;
+
+    const Poco::Crypto::RSAKey _key;
+    Poco::Crypto::RSADigestEngine _digestEngine;
+};
+
 class OAuth : public AuthBase
 {
 public:
@@ -50,7 +203,7 @@ public:
     }
 
     //TODO: This MUST be done over TLS to protect the token.
-    bool getAccessToken() override
+    const std::string getAccessToken() override
     {
         std::string url = _tokenEndPoint
                         + "?client_id=" + _clientId
@@ -70,7 +223,7 @@ public:
         Log::info("Response: " + reply);
         //TODO: Parse the token.
 
-        return true;
+        return std::string();
     }
 
     bool verify(const std::string& token) override


More information about the Libreoffice-commits mailing list