Can IHP be used for an app backend with JWT authentication? - ihp

Has anyone used IHP for an app backend and if so, what changes need to be made for that to be doable? Is there a jwt package or something like it that allows IHP to have JWT authentication? Does Digitally Induced support using IHP as an app backend?

IHP itself has JWT only as as part of DataSync right now. But here's a custom JWT implementation used at digitally induced:
{-# LANGUAGE AllowAmbiguousTypes #-}
module Application.Auth (initJWTAuthentication) where
import IHP.Prelude
import IHP.LoginSupport.Helper.Controller
import IHP.Controller.Session
import IHP.QueryBuilder
import IHP.Fetch
import IHP.ControllerSupport
import IHP.ModelSupport
import IHP.Controller.Context
import IHP.Controller.Param
import qualified Web.JWT as JWT
import qualified Data.ByteString as BS
import qualified Data.Maybe as Maybe
import qualified Data.Text as Text
{-# INLINE initJWTAuthentication #-}
initJWTAuthentication :: forall user normalizedModel.
( ?context :: ControllerContext
, ?modelContext :: ModelContext
, normalizedModel ~ NormalizeModel user
, Typeable normalizedModel
, Table normalizedModel
, FromRow normalizedModel
, PrimaryKey (GetTableName normalizedModel) ~ UUID
, GetTableName normalizedModel ~ GetTableName user
, FilterPrimaryKey (GetTableName normalizedModel)
, KnownSymbol (GetModelName user)
) => IO ()
initJWTAuthentication = do
let accessTokenQueryParam = (paramOrNothing "access_token")
let accessToken :: Maybe Text = accessTokenQueryParam <|> jwtFromAuthorizationHeader
case accessToken of
Just accessToken -> do
signer <- fromContext #JWT.Signer
let signature = JWT.decodeAndVerifySignature signer accessToken
case signature of
Just jwt -> do
let userId :: Id user = jwt
|> JWT.claims
|> JWT.sub
|> Maybe.fromMaybe (error "JWT missing sub")
|> JWT.stringOrURIToText
|> textToId
user <- fetchOneOrNothing userId
putContext user
Nothing -> error "Invalid signature"
Nothing -> pure ()
jwtFromAuthorizationHeader :: (?context :: ControllerContext) => Maybe Text
jwtFromAuthorizationHeader = do
case getHeader "Authorization" of
Just authHeader -> authHeader
|> cs
|> Text.stripPrefix "Bearer "
|> Maybe.fromMaybe (error "Invalid format of Authorization header, expected 'Bearer <jwt>'")
|> Just
Nothing -> Nothing
Drop that into Application/Auth.hs. Then change Web/FrontController.hs to call the initJWTAuthentication function like this:
instance InitControllerContext WebApplication where
initContext = do
setLayout defaultLayout
initAuthentication #User
initJWTAuthentication #User
After that you can make API requests to your IHP app like http://myapp/SomeAction?access_token=<JWT here> or by setting the Authorization HTTP header like Authorization: Bearer <JWT here>

Related

Encrypted access token request to google api failed with 400 code

Recently I come up a scenario where I need to encrypt a WEB API request and response using PyCryptodome inside Synapse notebook activity. I am trying to make a call to Google API, but the request should be encrypted and similarly response should be encrypted. After making the call with encrypted data, I am getting below error.
Error:
error code: 400, message: Invalid JSON Payload received. Unexpected Token, Status: Invalid argument.
I have written below code:-
import os
import requests
import json
import base64
from Crypto import Random
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from Crypto.Util.padding import pad,unpad
import secrets
key= os.urandom(16)
iv = Random.new().read(AES.block_size)
def encrypt_data(key, data):
BS = AES.block_size
pad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode()
cipher = AES.new(key, AES.MODE_CBC, iv)
encrypted_data = base64.b64encode(cipher.encrypt(pad(data)))
return encrypted_data
url = "https://accounts.google.com/o/oauth2/token"
client_Id = "XXXXX"
client_secret = "YYYYY"
grant_type = "refresh_token"
refresh_token = "ZZZZZZ"
access_type="offline"
data = {"grant_type":grant_type,
"client_id":client_Id,
"client_secret":client_secret,
"refresh_token":refresh_token,
"access_type":access_type
}
encode_data = json.dumps(data).encode("utf-8")
encrypt_data = encrypt_data(key,encode_data)
response = requests.post(url, data = encrypt_data)
print(response.content)
It would be really helpful if someone can give me idea or guide me on how I can achieve this.
Thank You!

How to display a Servant type signature as URL? [duplicate]

Suppose we have this simple API:
type FooAPI
= "foo"
:> QueryParam "age" Int
:> Get '[PlainText] Text
Is there a way to link type-level servant's API with a function that will generate URL for it? Like
someMagicFunction :: Proxy api -> SomeTypeFamily api
someMagicFunction = ?
generateURL :: Maybe Int -> Text
generateURL = someMagicFunction (Proxy #FooAPI)
>>> generateURL (Just 42)
"https://somehost/foo?age=42"
>>> generateURL Nothing
"https://somehost/foo"
I want to increase type-safety of URL generation so if I'll add some new parameter to FooAPI it will immediately appear in the type of generateURL and my code will not compile without editing the calls of generateURL.
I'm aware of servant-client library and I know that client function does somewhat similar but I couldn't figure out how to use this library in my case.
I would say that Servant.Links is exactly what you are looking for.
In particular, in your case I would use allLinks to generate a Link corresponding to the URL piece containing the path and query parameters:
>>> linkURI $ allLinks (Proxy #FooAPI) Nothing
foo
>>> linkURI $ allLinks (Proxy #FooAPI) (Just 18)
foo?age=18
Then I would transform the generated Link into an URI where I would specify required scheme and hostname. Here's a fully working module:
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
import Data.Proxy
import Data.Text (Text)
import Network.URI
import Servant.API
import Servant.Links
type FooAPI
= "foo"
:> QueryParam "age" Int
:> Get '[PlainText] Text
generateURL :: Maybe Int -> String
generateURL age = show uri
{ uriScheme = "https:"
, uriAuthority = Just nullURIAuth
{ uriRegName = "somehost" }
, uriPath = "/" <> uriPath uri
}
where
uri = linkURI link
link = allLinks (Proxy #FooAPI) age
And demonstration:
>>> generateURL (Just 42)
"https://somehost/foo?age=42"
>>> generateURL Nothing
"https://somehost/foo"

How do you authenticate a websocket connection with Knox token authentication on django channels?

I understand you can write custom authentication middleware to use in django channels 2.
This works fine with Django's built-in token authentication but using django-rest-knox tokens is a different story.
Knox stores its tokens in an encrypted form so it is not as easy as simply retrieving the user from the database by looking up the token.
Please help.
Figured it out!
from knox.auth import TokenAuthentication
...
knoxAuth = TokenAuthentication();
user, auth_token = knoxAuth.authenticate_credentials(tokenString.encode(HTTP_HEADER_ENCODING))
scope['user'] = user
Integrate the above code with: https://gist.github.com/rluts/22e05ed8f53f97bdd02eafdf38f3d60a
In order to be able to authenticate a user using token authentication, you must use cookies, The headers you can send using WS are limited, you must also implement your own "TokenAuthMiddleware" to handle the cookie. for channels 2, you also have to handle access to the database correctly, below is how to do that:
from channels.auth import AuthMiddlewareStack
from channels.db import database_sync_to_async
from knox.auth import TokenAuthentication
from django.contrib.auth.models import AnonymousUser
from django.db import close_old_connections
from rest_framework.exceptions import AuthenticationFailed
import re
class TokenAuthMiddlewareInstance :
def __init__ (
#
self ,
scope ,
middleware ,
):
self.middleware = middleware
self.scope = dict(scope)
self.inner = self.middleware.inner
async def __call__ (
#
self ,
receive ,
send ,
):
self.scope['user'] = AnonymousUser()
cookie = dict(self.scope.get('headers',{})).get(b'cookie')
if cookie :
token = re.findall(r'X-Authorization=(\w*)', cookie.decode('ascii'))
if len(token) :
self.scope['user'] = await self._g_user(token)
inner = self.inner(self.scope)
return await inner(receive, send)
#database_sync_to_async
def _g_user (
#
self ,
token ,
):
try :
token_key = token[0]
user, token = TokenAuthentication().authenticate_credentials(token_key.encode('ascii'))
close_old_connections()
return user
except AuthenticationFailed as e :
return AnonymousUser()
class TokenAuthMiddleware :
def __init__ (
#
self ,
inner ,
):
self.inner = inner
def __call__ (
#
self ,
scope ,
):
return TokenAuthMiddlewareInstance(scope, self)
TokenAuthMiddlewareStack = lambda inner: TokenAuthMiddleware(AuthMiddlewareStack(inner))

Get headers from http response

I am new to elm,
I have a login api which returns a JWT token in its hedears
curl http://localhost:4000/api/login?email=bob#example&password=1234
response:
HTTP/1.1 200 OK
authorization: Bearer eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXyLp0aSI6ImefP2GOWEFYWM47ig2W6nrhw
x-expires: 1499255103
content-type: text/plain; charset=utf-8
success
now Im trying to write a function that will send request and return the token from the headers in elm
authUser =
Http.send "http://localhost:4000/api/login?email=bob#example&password=1234"
how do I do this in a simple way?
In order to extract a header from a response, you will have to use Http.request along with the expectStringResponse function, which includes the full response including headers.
The expectStringResponse function takes a Http.Response a value, so we can create a function that accepts a header name and a response, then returns Ok headerValue or Err msg depending on whether the header was found:
extractHeader : String -> Http.Response String -> Result String String
extractHeader name resp =
Dict.get name resp.headers
|> Result.fromMaybe ("header " ++ name ++ " not found")
This could be used by a request builder like so:
getHeader : String -> String -> Http.Request String
getHeader name url =
Http.request
{ method = "GET"
, headers = []
, url = url
, body = Http.emptyBody
, expect = Http.expectStringResponse (extractHeader name)
, timeout = Nothing
, withCredentials = False
}
Here is an example on ellie-app.com which returns the value of content-type as an example. You can substitute "authorization" for your purposes.
May I humbly suggest you look at my elm-jwt library, and the get function there?
Jwt.get token "/api/data" dataDecoder
|> Jwt.send DataResult
JWT tokens normally need to be sent as a Authorization header and this function helps you create a Request type that can be passed to Http.send or Jwt.send

JSONP response in Lift Framework

Hi I am using Lift for my api and need some help generating a jSONP response. I have a working version of the JSON response and need to add to it so that I can use my api for cross-domain calls. Here is a code snippet of my api currently:
/Request/
case "api" :: "events" :: "person" :: _ Get req => JsonResponse(json_person(personEvents(req, req.request.queryString.mkString("")),person_details(req)))
/response/
def json_person(in : List[Events], person1 : List[Person]) : JValue = {
("person" ->
person1.map(people =>
("name" -> people.main_person_name.is) ~
("alternate_name" -> people.aka.is)
))}
The way I've done this in the past is to check for whether or not a "callback" query parameter was provided in the URL, and if so, use the function name provided to perform the callback. If not provided, send back the object itself.
case "api" :: "events" :: "person" :: _ Get req => {
val jsonObj = json_person(...)
S.param("callback") match {
case Full(callbackName) => JSFunc(callbackName, jsonObj).cmd
case _ => JsonResponse(jsonObj)
}
}