google-oauth2-jwt 0.1.3 → 0.2.0
raw patch · 3 files changed
+60/−52 lines, 3 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Network.Google.OAuth2.JWT: instance GHC.Classes.Eq Network.Google.OAuth2.JWT.SignedJWT
+ Network.Google.OAuth2.JWT: instance GHC.Show.Show Network.Google.OAuth2.JWT.SignedJWT
- Network.Google.OAuth2.JWT: getSignedJWT :: Email -> Maybe Email -> [Scope] -> Maybe Int -> PrivateKey -> IO (Either String ByteString)
+ Network.Google.OAuth2.JWT: getSignedJWT :: Email -> Maybe Email -> [Scope] -> Maybe Int -> PrivateKey -> IO (Either String SignedJWT)
Files
- LICENSE +1/−1
- google-oauth2-jwt.cabal +10/−9
- src/Network/Google/OAuth2/JWT.hs +49/−42
LICENSE view
@@ -1,4 +1,4 @@-google-oauth2-jwt - Copyright Michel Boucey (c) 2016+google-oauth2-jwt - Copyright Michel Boucey (c) 2016-2017 All rights reserved.
google-oauth2-jwt.cabal view
@@ -1,13 +1,14 @@ name: google-oauth2-jwt-version: 0.1.3+version: 0.2.0 synopsis: Get a signed JWT for Google Service Accounts-description: Please see README.md+description: This library implements the creation of the+ signed JWT for Google Service Accounts. homepage: https://github.com/MichelBoucey/google-oauth2-jwt license: BSD3 license-file: LICENSE author: Michel Boucey maintainer: michel.boucey@cybervisible.fr-copyright: (c) 2016 - Michel Boucey+copyright: (c) 2016-2017 - Michel Boucey category: Google build-type: Simple extra-source-files: README.md@@ -16,13 +17,13 @@ library hs-source-dirs: src exposed-modules: Network.Google.OAuth2.JWT- build-depends: base >= 4.7 && < 5- , base64-bytestring >= 1.0.0 && < 1.1- , bytestring >= 0.10.6 && < 0.11+ build-depends: base >= 4.7 && < 5+ , base64-bytestring >= 1.0.0 && < 1.1+ , bytestring >= 0.10.6 && < 0.11 , HsOpenSSL >= 0.11.1.1 && < 0.12- , RSA >= 2.1.0.3 && < 2.3- , text >= 1.2.2 && < 1.3- , unix-time >= 0.3.6 && < 0.4+ , RSA >= 2.1.0.3 && < 2.3+ , text >= 1.2.2 && < 1.3+ , unix-time >= 0.3.6 && < 0.4 default-language: Haskell2010 GHC-options: -Wall
src/Network/Google/OAuth2/JWT.hs view
@@ -19,13 +19,14 @@ ) where import Codec.Crypto.RSA.Pure+import Control.Monad (unless) import qualified Data.ByteString as B import Data.ByteString.Base64.URL (encode) import Data.ByteString.Lazy (fromStrict, toStrict) import Data.Maybe (fromMaybe, fromJust) import Data.Monoid ((<>)) import qualified Data.Text as T-import Data.Text.Encoding+import Data.Text.Encoding (encodeUtf8) import Data.UnixTime (getUnixTime, utSeconds) import Foreign.C.Types import OpenSSL.EVP.PKey (toKeyPair)@@ -33,19 +34,23 @@ readPrivateKey) import OpenSSL.RSA -type Scope = T.Text+data SignedJWT =+ SignedJWT !B.ByteString+ deriving (Eq, Show) type Email = T.Text +type Scope = T.Text+ -- | Get the private key obtained from the--- the Google API Console from a PEM file.+-- Google API Console from a PEM file. fromPEMFile :: FilePath -> IO PrivateKey fromPEMFile f = readFile f >>= fromPEMString -- | Get the private key obtained from the -- Google API Console from a PEM 'String'. ----- >fromPEMString "-----BEGIN PRIVATE KEY-----\nB9e ... bMdF\n-----END PRIVATE KEY-----\n"+-- >fromPEMString "-----BEGIN PRIVATE KEY-----\nB9e [...] bMdF\n-----END PRIVATE KEY-----\n" -- > fromPEMString :: String -> IO PrivateKey fromPEMString s =@@ -53,10 +58,11 @@ \k -> return PrivateKey { private_pub =- PublicKey { public_size = rsaSize k- , public_n = rsaN k- , public_e = rsaE k- }+ PublicKey+ { public_size = rsaSize k+ , public_n = rsaN k+ , public_e = rsaE k+ } , private_d = rsaD k , private_p = rsaP k , private_q = rsaQ k@@ -70,41 +76,42 @@ -- -- >grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion= ---getSignedJWT :: Email- -- ^ The email address of the service account.- -> Maybe Email- -- ^ The email address of the user for which the- -- application is requesting delegated access.- -> [Scope]- -- ^ The list of the permissions that the application requests.- -> Maybe Int- -- ^ Expiration time (maximun and default value is an hour, 3600).- -> PrivateKey- -- ^ The private key gotten from the PEM string obtained from the- -- Google API Console.- -> IO (Either String B.ByteString)- -- ^ Either an error message or a signed JWT.+getSignedJWT+ :: Email+ -- ^ The email address of the service account.+ -> Maybe Email+ -- ^ The email address of the user for which the+ -- application is requesting delegated access.+ -> [Scope]+ -- ^ The list of the permissions that the application requests.+ -> Maybe Int+ -- ^ Expiration time (maximun and default value is an hour, 3600).+ -> PrivateKey+ -- ^ The private key gotten from the PEM string obtained from the+ -- Google API Console.+ -> IO (Either String SignedJWT)+ -- ^ Either an error message or a signed JWT. getSignedJWT iss msub scs mxt pk = do let xt = fromIntegral (fromMaybe 3600 mxt)- if xt >= 1 && xt <= 3600- then do- cs <- do- let s = maybe T.empty (\e -> "\"sub\":\"" <> e <> "\",") msub- (t',xt') <- getUnixTime >>=- \t -> return (toText (utSeconds t),toText (utSeconds t + CTime xt))- return $- toJWT $- "{\"iss\":\"" <> iss <> "\"," <> s <> "\"scope\":\"" <>- T.intercalate " " scs <> "\",\"aud\":\"https://www.goo\- \gleapis.com/oauth2/v4/token\",\"exp\":" <> xt' <> ",\"\- \iat\":" <> t' <> "}"- let i = toJWT "{\"alg\":\"RS256\",\"typ\":\"JWT\"}" <> "." <> cs- return $- case rsassa_pkcs1_v1_5_sign hashSHA256 pk (fromStrict i) of- Right s -> Right (i <> "." <> encode (toStrict s))- Left _ -> Left "RSAError"- else fail "Bad expiration time"+ unless (xt >= 1 && xt <= 3600) (fail "Bad expiration time")+ t <- getUnixTime+ let i = header <> "." <> toB64 ("{\"iss\":\"" <> iss <> "\","+ <> maybe T.empty (\e -> "\"sub\":\"" <> e <> "\",") msub+ <> "\"scope\":\"" <> T.intercalate " " scs <> "\",\"aud\+ \\":\"https://www.googleapis.com/oauth2/v4/token\",\"ex\+ \p\":" <> toT (utSeconds t + CTime xt) <> ",\"iat\":"+ <> toT (utSeconds t) <> "}")+ return $+ either+ (fail "RSAError")+ (\s -> return $ SignedJWT $ i <> "." <> encode (toStrict s))+ (rsassa_pkcs1_v1_5_sign hashSHA256 pk $ fromStrict i) where- toText = T.pack . show- toJWT = encode . encodeUtf8+ toT = T.pack . show++header :: B.ByteString+header = toB64 "{\"alg\":\"RS256\",\"typ\":\"JWT\"}"++toB64 :: T.Text -> B.ByteString+toB64 = encode . encodeUtf8