How is the Jwt process on the client side using flutter? - flutter

I am new using JWT, could someone explain to me how is the process step by step?
I am trying to validate a login and if the password is correct I receive a token with some information.
What do I do with that token in flutter? Do I decode it and use that information or what do I do?
When I make different requests, should I decode the token to do some http and then encode on the client side?
Your help would be greatly appreciated.
here is my code in flutter
// code
Future validateLogin(passwordLogin,emailLogin,BuildContext context) async {
try{
Map<String, String> headers = {
'Content-Type':'application/json;charset=UTF-8',
'Charset':'utf-8'
};
var Url= Uri.parse("http://");
var response = await http.post(Url,
body:{
'password' : passwordLogin,
'email' : emailLogin,
},
).timeout(const Duration(seconds: 90)) ;
var data = json.decode(response.body);
if(data['estado'] == "CORRECT"){
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setBool("CheckEstadoLogin", true);
await prefs.setString("GuardarToken", data['token'].toString());
Navigator.push(context,
new MaterialPageRoute(builder:
(context) => new MenuP())
);
}else {
Fluttertoast.showToast(
msg: "Incorrect",
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
timeInSecForIosWeb: 1,
backgroundColor: Colors.green,
textColor: Colors.white,
fontSize: 16.0
);
}
}on TimeoutException catch(e){
print("Tardo mucho la conexion");
}on Error catch(e){
print("Error de Http");
}
}
}

In short, you would find that your backend(if it is indeed providing secure services and not public) will not allow you to consume any webservices unless you have a required access token. So the purpose of jwt in the authorisation flow was to obtain the same that you could have used in the subsequent API calls(mostly the access token is passed as part of the header Authorization). If the APIs you are trying to consume from the backend are public, then there is no need for the access token anyways. So it should be reverse of what you are thinking. The first part is, do you have API calls that needs to be made that will need an access token? If you do, then do the login, get the access token etc, so that it can be used when you make these API calls from your flutter app.
Flutter is the client side. The normal flow when you do a login is, to pass on your user credentials via an interface to a Identity provider in the backend which might use a LDAP or some other data source to validate the user. After which you receive a jwt token back (There can be MFA or other complex secure flows in between(refer oauth2 authorisation code? authorisation code with pkce) but the ultimate objective is to get back a access token.
Now once you have the token, the client is expected to pass this access token in all subsequent Web service calls it makes to the backend. A combination of a gateway + identity provider will ensure to validate that the access token passed for the WS is right before sending the request to the actual server which will service that web service request made by the flutter app.
Decoding jwt at client side, would also give you certain details which the client(flutter app) is already aware of, and may not be of much value.Once again there are best practices in oauth2 on how to refresh this access token on intervals by leveraging a grant etc etc.
This is a good read if you are finding the RFCs a bit complex.
https://auth0.com/docs/get-started/authentication-and-authorization-flow

Related

Authentication flow with Oauth2 in flutter communicating with own api

After some hours of research in vain I stay confused how to do the following:
I have a flutter app which authenticates via OAuth2 to Google (google_sign_in) and Facebook. For Facebook this is the code:
final LoginResult loginResult = await FacebookAuth.instance.login();
final userData = await FacebookAuth.instance.getUserData();
print(userData);
Which prints: {email: john.doe#email.com, id: 123456, name: John Doe}
I already have a webpage with OAuth2 authentication built in Flask/Python. Now I want my users to be able to both use Web and App and share the preferences/data/etc.
How would I achieve that? In my Flask webapp I'm just creating a user in my database if it doesn't exist and then use some authentication headers in subsequent calls. So I thought with the app I could…
send what I got from OAuth to the api and create the user if it does not yet exist
return some sort of token (with a TTL?)
verify the tokens being sent by the app
But this is a lot of custom boilerplate code, I'm sure that this is existing somewhere/somehow. Additionally: How can I be sure someone is not "tampering" my app via decompile, proxying or just plainly calls my api and claiming to be someone else?
My security requirements are medium: The app will eventually have messaging but won't be used for things like money transfer.
I'm considering these options:
PKCE but this looks like the OAuth2 flow would go through my flask api and that sounds too complex (I had a hard time already getting OAuth2 to work in flutter alone)
Resource Owner Password Credentials Grant which sounds like I can somehow pass the results of OAuth2 to my api, get back a token and use this in subsequent requests. However this seems like an outdated protocol (top google results are articles from oracle)
firebase implementation: they use the same flow: first OAuth2 authentication and then passing the credentials into their servers api. On the first time they pass the credentials a user is created and stored in the database, etc. But my reverse engineering skills are not good enough to figure out how it's done.
using a webview and use the oauth2 of my flask website. I'm shying back from this because it would be not a nice mobile experience plus I would not know how to read/store these credentials
After a lot of reading I found a good article on auth0 , in essence there are two options:
Resource Owner Password Flow - use this if you totally trust your app, e.g. when you deploy it to a closed group of users for which you have device management in place. This situation doesn't apply for me and also Auth0 doesn't recommend it. Still, it would have been relatively easy to implement.
PKCE (Proof Key for Code Exchange) - use this when the client cannot be trusted (IMO 99.9% of mobile apps). But this needs some fancy protocol between the mobile app and the server and alone by looking at the flowchart diagram I got headaches
As PKCE looks too complicated to implement myself I decided to go with Firebase, which helps small projects such as mine where you don't want to go through the pain to code the whole PKCE flow yourself.
What I did was:
adding firebase authentication to my flask app, using flask-firebase - this was worth it since it decreased the lines of python code by 40%. Because the module lacks good documentation I wrote this blog post which explains how to use it
adding firebase authentication to flutter. This is very well documented e.g. here
The whole flow then works like this:
flutter triggers the oauth flow for e.g. google
flutter gets back the auth details, including email address, name, etc. (depends on oauth provider)
the auth details are sent to firebase which creates the user if it doesn't exist yet, enriches it with a user id and packs it into an encrypted token
the token is sent to flask, which verifies the token against firebase
flask logs the user in (via flask_login) and returns a session cookie
the session cookie is stored in flutter (using requests) and used for subsequent api calls
to preserve the user logged in even after app close, the session is stored in apps preferences (using shared_preferences)
In essence, this is the code needed (google social login example):
Future<String?> signInWithGoogle() async {
final GoogleSignInAccount? googleUser = await GoogleSignIn().signIn();
final GoogleSignInAuthentication? googleAuth =
await googleUser?.authentication;
final credential = GoogleAuthProvider.credential(
accessToken: googleAuth?.accessToken,
idToken: googleAuth?.idToken,
);
UserCredential userCredentials =
await FirebaseAuth.instance.signInWithCredential(credential);
return userCredentials.user?.getIdToken();
}
…
var cookies = await Requests.getStoredCookies('example.com');
SharedPreferences? prefs;
if (!cookies.keys.contains('session')) {
prefs = await SharedPreferences.getInstance();
if (prefs.containsKey('session')) {
print('cookie not set, load session from prefs');
await Requests.addCookie(
'example.com', 'session', prefs.getString('session')!);
}
}
cookies = await Requests.getStoredCookies('example.com');
if (!cookies.keys.contains('session')) {
print('cookie not set, prefs contain no session, signing in');
String? idToken = await signInWithGoogle();
if (idToken != null) {
await Requests.post('https://example.com/auth/sign-in',
body: idToken,
headers: {'Content-Type': 'application/jwt'},
bodyEncoding: RequestBodyEncoding.PlainText);
var cookies = await Requests.getStoredCookies('example.com');
prefs?.setString('session', cookies['session']!.value);
}
}
var r = await Requests.get('https://example.com/api/something_which_requires_login');
The important part happens with Requests.post: this posts the idToken of firebase to flask, which in turn then verifies the token, calls login_user and returns response with the session cookie header. This cookie is stored by requests and is added to subsequent http requests.
Because this is some mouthful I created this blogpost which explains this in more detail.

How do I call my Cognito secured RestAPI from the browser when using next-auth?

I have an AWS RestApi secured by AWS Cognito. In addition to this I have a NextJS app using next-auth that provides user authentication against the Cognito User Pool.
I now want to call the RestApi directly from the browser, but cannot find a way to include the proper credentials. Since the only cookies present in my web-app begin with next-auth- I assume they are not suitable for the task.
So, how do I access the access token from the browser?
I ended up hooking up to two callbacks on the NextAuth configuration, like this:
NextAuth({
providers: [
CognitoProvider({
idToken: true,
issuer,
clientSecret,
clientId,
authorization,
}),
],
callbacks: {
session: async function ({ session, token }) {
return {
...session,
bearerToken: token.bearerToken ?? session.bearerToken,
};
},
async jwt({ token, account }) {
token.bearerToken = account?.id_token ?? token.bearerToken;
return token;
},
},
})
From the pages/api/auth/[...nextauth].ts file.
This makes the bearerToken available via the getSession call:
import { getSession } from "next-auth/react";
// ...
const { bearerToken } = await getSession();
You can use application like Postman to pass the Authorization header. I am not sure if your query is about getting the access token or about how to use the access token.
In order to get the access token and then to call your REST API, you need to have a server. Suppose you have your server with domain name "example.com". Then, put this server name in the callback URL of your Cognito user pool's app client.
After user authentication, Cognito will send the access token to "example.com". Then, you server will have the responsibility to correctly call the REST API with the access or ID token provided by Cognito.
For testing purpose, you can manually get the access token after authenticating with Cognito. Then, you can use application like Postman to make HTTP calls to your REST API endpoint with the access token. Put the token as value to the authorization header.

flutter making authenticated api requests

I am trying to do something along these lines to make an authenticated api request:
Future<http.Response> fetchAlbum() {
return http.get(
'https://jsonplaceholder.typicode.com/albums/1',
// Send authorization headers to the backend.
headers: {HttpHeaders.authorizationHeader: "Basic your_api_token_here"},
);
}
I get my api tokens by calling
FirebaseUser user = FirebaseAuth.instance.signInWithEmailAndPassword(email: _email, password: _password).
My questions are:
Don't these tokens expire after a short period of time? How do I make it so my user doesn't have to constantly log in? I don't understand this at all.
Should I save my user variable in a global provider state and access it this way?
I've been watching tons of tutorials on this and I don't get it.

Are IBM Watson IAM tokens good for all services or specific to each service, e.g., Speech-to-Text?

IBM's documentation says that the following Node back end code enables you to Use the API key to have the SDK manage the lifecycle of the token. The SDK requests an access token, ensures that the access token is valid, and refreshes it if necessary.
const SpeechToTextV1 = require('ibm-watson/speech-to-text/v1');
const { IamAuthenticator } = require('ibm-watson/auth');
const speechToText = new SpeechToTextV1({
authenticator: new IamAuthenticator({
apikey: '{apikey}',
}),
url: '{url}',
});
How do I get the token from speechToText to pass to my front end Angular app running in the browser? I tried calling the method getToken to get the token:
const SpeechToTextV1 = require('ibm-watson/speech-to-text/v1');
const { IamAuthenticator } = require('ibm-watson/auth');
const speechToText = new SpeechToTextV1({
authenticator: new IamAuthenticator({
apikey: 'my-api-key',
}),
url: 'my-url',
});
speechToText.getToken(function (err, token) {
if (!token) {
console.log('error: ', err);
} else {
console.log(token);
// do more stuff with the token
}
});
That didn't work. The error message is speechToText.getToken is not a function. Should I try speechToText.authenticator.getToken?
I tried getting the token from ibm-watson/sdk instead of from ibm-watson/speech-to-text/v1?
const watson = require('ibm-watson/sdk');
const { IamAuthenticator } = require('ibm-watson/auth');
const authorization = new watson.AuthorizationV1({
authenticator: new IamAuthenticator({ apikey: 'my-api-key' }),
url: 'my-url'
});
authorization.getToken(function (err, token) {
if (!token) {
console.log('error: ', err);
} else {
console.log(token);
// do stuff with token
}
});
That gets a smokin' new token. But the token doesn't work. When I run WatsonSpeech.SpeechToText.recognizeMicrophone I get an error message HTTP Authentication failed; no valid credentials available.
It appears that each IBM Watson service needs its own token, created with a service-specific URL. I put the Speech-to-Text URL into ibm-watson/sdk so I should get the right token. I don't see why the token isn't working.
Take a look at the supplying credentials section of the README in the Node SDK about managing the token yourself if that's what you want to do:
Use the BearerTokenAuthenticator if you want to manage the lifecycle
yourself. For details, see Authenticating to Watson services. If you want
to switch your authenticator, you must override the authenticator
property directly.
There's a link from that "Authenticating" topic that might help you understand the access process. See Invoking IBM Cloud service APIs
IBM Cloud uses what it calls Identity and Access Management (IAM) to manage access to resources. IAM has several concepts which allow for fine-grained security control. You can grant scoped access privileges to users or roles. Thus, one user may be manager for a resource, another user only reader.
Now, to access a service like the IAM-controlled Watson services, your username / password or API key is turned into a Bearer and a Refresh token, the Bearer token is only valid for a certain time and then needs a fresh Refresh token. This could be a reason why you see different tokens.
You may have seen the underlying core Node.js SDK which has background information on Authentication and some functions.
Long story short: When you have successfully created the IamAuthenticator, you should be able to request the token and use it. Even better, you can pass the IamAuthenticator to many services, including the Watson services, to initialize a session. The code "knows" how to obtain the authentication information and use it to authenticate for the other service.

Securing a restful API verifying clients with JWT

I have a RESTful API using JWT for authentication purposes. The first call I receive from a client is the /login call, with the following payload (no headers)
{
"username" : xxxx,
"password": wwww
}
The server verifies if the user is registered and then returns a signed JWT to the client in order to be received in the next calls.
I wonder if this is secure enough. I don't check anywhere if the client sends me a client id / client secret (like in OAuth) so I cannot verify if this call is from my webapp / apps or if it is an external client which I don't know about. I want to know if it makes sense to implement this behavior using JWT and how to implement it.
(I know how to do it with OAuth2 but I don't want to move now from JWT authentication)
Thank you!
If I understood you correctly, you should create a function somewhat similar to this:
function verify(req, res, next) {
const token = req.header('x-auth-token');
if (!token) {
return res.status(401).json({
msg: 'No token, auth denied'
});
}
try {
const decoded = jwt.verify(token, config.get(YOUR_SECRET_GOES_HERE));
req.user = decoded.user;
next();
} catch (err) {
res.status(401).json({
msg: 'Token is not valid'
});
}
}
For all secured API endpoints you should apply it like this:
router.get('/anyuserinfo', verify, (req, res) => ...
And that is it. The function will send 401 response if no token is provided.
I think I found another Stackoverflow question that answers mine:
JWT (Json Web Token) Audience "aud" versus Client_Id - What's the difference?
In a nutshell, client_id and client_secret should be sent on headers to the server to be validated before sending a new JWT token.