What api authentication method should be used for server to server communication? - api

I have a webapp in flask where users can login with email and password. Connected to the same database I have an api where those same users will use it programmatically. When they make requests I need to know who's making the request.
I read about athentication and authorization, but I'am confused about what's the best method for my use case. I focused on JWT tokens but the expiration of the tokens makes me think it's not the best in this scenario.
How should the server login programmatically when the token expires and so on? Is there a common way to do what I pretend?

Use jwt to auth api(i use Flask-JWT-Extended)
example:
def login_required(func):
#wraps(func)
def decorate(*args, **kwargs):
verify_refresh_token()
identify = get_jwt_identity()
expires_time = datetime.fromtimestamp(get_raw_jwt().get('exp'))
remaining = expires_time - datetime.now()
# auto refresh token if token expiring soon
refresh_space = current_app.config['JWT_MIN_REFRESH_SPACE']
# store the token in requests.g object
if refresh_space and remaining < refresh_space:
g.refresh_token = create_refresh_token(identity=identify)
g.id = identify
return func(*args, **kwargs)
return decorate
#login_required
def view_func():
pass
# return json
When token will expire, the func will auto refresh token, you can get new token in requests.g object and then return to user.
quote
quote

Related

How does a Servant client handle received cookies?

I want to use a Servant client to first call a login endpoint to obtain a session cookie and then make a request against an endpoint that requires cookie authentication.
The API is (simlified)
import qualified Servant as SV
import qualified Servant.Auth.Server as AS
import qualified Servant.Client as SC
-- Authentication and X-CSRF cookies
type CookieHeader = ( SV.Headers '[SV.Header "Set-Cookie" AS.SetCookie
, SV.Header "Set-Cookie" AS.SetCookie]
SV.NoContent )
type LoginEndpoint = "login" :> SV.ReqBody '[SV.JSON] Login :> SV.Verb 'SV.POST 204 '[SV.JSON] CookieHeader
type ProtectedEndpoint = "protected" :> SV.Get '[SV.JSON]
-- The overall API
type Api = LoginEndpoint :<|> (AS.Auth '[AS.Cookie, AS.JWT] User :> ProtectedEndpoint)
apiProxy :: Proxy Api
apiProxy = Proxy
I define the client as follows:
loginClient :: Api.Login -> SC.ClientM Api.CookieHeader
protectedClient :: AC.Token -> SC.ClientM Text :<|> SC.ClientM SV.NoContent
loginClient :<|> protectedClient = SC.client Api.apiProxy
How does the client handle the authentication cookie? I can think of two ways. When executing a request in the ClientM monad, like
do
result <- SC.runClientM (loginClient (Login "user" "password")) clientEnv
[..]
where Login is the login request body and clientEnv of type Servant.Client.ClientEnv, the cookie could part of the result, it could be updated in the cookieJar TVar inside clientEnv, or both. I would assume the TVar to be updated, so that a subsequent request with the same clientEnv would send the received cookies along. However, my attempt to read the TVar and inspect its contents using Network.HTTP.Client.destroyCookieJar revealed an empty array. Is this intended? I couldn't find anything in the documentation.
Thus, to make an authenticated call, I would need to extract the cookie from the header in the result (how?), update the TVar, create a new clientEnv that references this TVar, and make the authenticated call using this new environment. Is this indeed the suggested procedure? I'm asking because I suppose the use case is so standard that there should be a more streamlined solution. Is there? Am I missing something?
After some experimentation, I figured out that the Servant client indeed does maintain cookies in the cookieJar that is part of the clientEnv. To be more precise, clientEnv contains the field cookieJar, which is of type Maybe (TVar CookieJar). It is the TVar the client updates according to the Set-Cookie instructions of subsequent requests. It is up to the developer to create and initialize that TVar before making the first request; otherwise, the Servant client will discard cookies between requests.
In addition, it is possible to retrieve cookies in the same way as the request body. To this end, the cookies to be retrieved must be defined as part of the API type, like in the example of my original question:
type LoginEndpoint = "login" :> SV.ReqBody '[SV.JSON] Login :> SV.Verb 'SV.POST 204 '[SV.JSON] CookieHeader
Disassembling the returned was a little tricky at first, because I needed to figure out the final type that results from Servant's type-level machinery. Ultimately, I did the following:
SV.Headers resp h <- tryRequest clientEnv (loginClient (Api.Login "user" "pwd"))
let headers = SV.getHeaders h
where tryRequest is a helper to execute runClientM and extract the Right part. The pattern match resp contains the return value (here NoContent), while h is is an HList of the different headers. It can be converted into a regular list of Network.HTTP.Types.Header using Servant's getHeaders function.
It would then be possible to change or generate new headers and submit them with a new request by adding a new header to the cookieJar TVar (see the cookie-manipulating functions in Network.HTTP.Client).

Flask JWT to SQLAlchemy User Object?

I have an app where the user details are passed as a JWT containing information about the current user and it's roles.
Everytime the user is logged in (via a KeyCloak instance), the information from the JWT is parsed on my end in a function that updates the user object via SQLAlchemy. However, since there is no user object being passed back and forth in the backend, I have to parse the JWT for roles for every action that requires it. I also have a need for auditing, and due to the structure of the app, this module does not necessarily have access to the request objects at the time of logging.
I'm looking for a neat way to make something like flask_users current_user() functionality work by mapping JWT -> ORM user object, to be able to transparently get the current user. Is there any way to go about this? The user registration and so on is completely separate from the app, and Flask only knows which user it is based on tokens in the requests that are being sent.
TLDR; Is there a way to load a user from the DB based on an already issued JWT (which contains information to map to a user), and is there perhaps already a lib or extension to flask that supports this?
I use a decorator to parse the JWT token using pyjwt.
Then from the parsed token you can get the user and do the proper authorization.
If you don't want to add the decorator to all your functions that require authorization you can use Flasks before_request.
from functools import wraps
from flask import Response, current_app, request
from jwt import decode
from jwt.exceptions import (DecodeError, ExpiredSignatureError,
InvalidSignatureError)
def authorize(func):
#wraps(func)
def check_authorization(*args, **kwargs):
try:
jwt_token = request.cookies.get('auth_token') # get token from request
if jwt_token is None:
return Response(status=401, headers={'WWW-Authenticate': 'Bearer'})
token = decode(
jwt_token,
key='pub_key', # public key to validate key
algorithms=['RS256'], # list of algs the key could be signed
verify=True
)
# you can call another function to do check user roles here
# e.g authorize(token['sub'])
return func(*args, **kwargs)
except (InvalidSignatureError, DecodeError, ExpiredSignatureError):
return Response(
response='{ "error": "token_invalid"}',
status=401,
headers={'WWW-Authenticate': 'Bearer'})
return check_authorization
This is supported with flask-jwt-extended: https://flask-jwt-extended.readthedocs.io/en/stable/complex_objects_from_token/

how to use django REST JWT authorization and authentication in class based views

I am using JWT authentication I am using this type of authorization app wide.
I am trying to figure out how to ue it in a view.
Example. Say I only want to allow a user to create an approved venue if they have the correct permissions. What would I add to this view to get access to the user?
I know that django has request.user but how do I turn that on? Is it always on and request.user is null if there is no token passed into the header? Or is it middleware? The problem I am ultimately having is there is a lot of info getting to this point, but very little on actually using the JWT on the view.
please help.
# for creating an approved venue add ons later
class CreateApprovedVenue(CreateAPIView):
queryset = Venue.objects.all()
serializer_class = VenueSerializer
Django rest framework jwt docs
https://jpadilla.github.io/django-rest-framework-jwt/
Django rest framework permissions docs
http://www.django-rest-framework.org/api-guide/permissions/
so I discovered this resource and looking at it now.
https://code.tutsplus.com/tutorials/how-to-authenticate-with-jwt-in-django--cms-30460
This example is sheading light:
# users/views.py
class CreateUserAPIView(APIView):
# Allow any user (authenticated or not) to access this url
permission_classes = (AllowAny,)
def post(self, request):
user = request.data
serializer = UserSerializer(data=user)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
To use JWT authentication, you need to do the following installation steps: https://jpadilla.github.io/django-rest-framework-jwt/#installation
Once that is done, you can include the auth by simply adding the authentication_classes as follows
# for creating an approved venue add ons later
class CreateApprovedVenue(CreateAPIView):
authentication_classes = (JSONWebTokenAuthentication, )
queryset = Venue.objects.all()
serializer_class = VenueSerializer
And you have user available to you as request.user in all the request methods. In the case of the CreateAPIView you can do:
def post(self, request, *args, **kwargs):
user = request.user
...

Sagepay token system delete token

I'm using Sage Pay token system, evrithyng is working fine I store the tokens on my side. The question is if I want to remove a token is it fine to remove it only from my side, and then if some one wants to add tha card again to create another token or I have to send a request to sagepay with StoreToken = 0 param?
You can remove the token from your side only, but I would recommend sending a REMOVETOKEN request to Sage Pay to remove it (or setting StoreToken=0 on usage) - token storage is charged above a certain threshold. No point paying for something you can't use.....
Your end user can create another token if required.
Here goes the code:
# REMOVE TOKEN REQUEST
if(YourCondition=='OK')
{
$strRemoveTokenURL = "https://test.sagepay.com/gateway/service/removetoken.vsp";
$sToken = 'Token Stored your side';
$strPost = "VPSProtocol=3.00&TxType=REMOVETOKEN&Vendor=yourvendorid&Token=".$sToken;
$arrRemoveResponse = requestPost($strRemoveTokenURL, $strPost);
}
# REMOVE TOKEN RESPONSE
echo '<hr>';
print"<pre>";print_r($arrRemoveResponse);print"<pre>";
exit;

Invalid twitter oauth token for Abraham's Oauth class

Here's my current operations:
1./ User accepts app and the app callback stores the oauth_token and oauth_token_secret into the database (as access_token and access_token_secret).
2./ We use a cli script to handle autoposting to twitter. We load the twitter oauth object as follows:
public function post()
{
$consumerKey = $this->getConsumerKey();
$consumerSecret = $this->getConsumerSecret();
$accessToken = $this->getAccessToken();
$accessSecret = $this->getAccessSecret();
$twitteroauth = new TwitterOAuth($consumerKey,$consumerSecret,$accessToken,$accessSecret);
$message = $this->getPostMessage();
$result = $twitteroauth->post('statuses/update', array('status' =>$message));
$this->log($result);
}
Now this assumes we are using the API consumer key and secret assigned to the app and the user's stored access tokens.
The result we are getting is:
Invalid or expired token
I don't quite understand why we are receiving this. We are using the access token and access token secret provided to us by twitter.
I did notice that there is a GET parameter oauth_verifier. This isn't something we need to be using somewhere?
In any case, I'm not quite sure whats wrong here.
Do I need to log in or something before doing posting?
your code is correct.
The problem is that the library's Util.urlParameterParse() method is broken.