there guys, I do have an interesting problem here and I would be really glad if any of you it will be able to help me with that.
What's my app flow:
Register with the email, password and some other details:
User firebase in order to auth the user and create an account via email and password, at the same time I'm writing the custom data of the user to the database.
Log in the user.
That's it, that's all my basic logic, and how you can see I'm not doing any reading from the DB so far as I know.
Now... the problem is that from some weird reason when I'm registering my user I'm going to the firebase console to see the usage of my DB and I will see something like... for one user which was created I will have 1 write (which is fine as I was expected) but also 13-20 READS FROM DB.
Now that's my question, WHY on earth I have reads on firestorm when I'm doing just auth and writes?
Here it's my DB code which I'm using right now.
class DatabaseFirebase implements BaseDataBase {
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
final FirebaseStorage _storage = FirebaseStorage.instance;
FirebaseUser _firebaseUser;
Firestore _firestore = Firestore.instance;
#override
Future<String> login(String email, String password) async {
_firebaseUser = await _firebaseAuth.signInWithEmailAndPassword(
email: email, password: password);
return _firebaseUser.uid;
}
#override
Future<String> register(String email, String password) async {
_firebaseUser = await _firebaseAuth.createUserWithEmailAndPassword(
email: email, password: password);
return _firebaseUser.uid;
}
#override
Future<UserData> getCurrentUser() async {
if (_firebaseUser == null)
_firebaseUser = await _firebaseAuth.currentUser();
UserData user = UserData();
user.email = _firebaseUser?.email;
user.name = _firebaseUser?.displayName;
return user;
}
#override
Future<void> logout() async {
_firebaseAuth.signOut();
}
#override
Future<void> onAuthStateChanged(void Function(FirebaseUser) callback) async {
_firebaseAuth.onAuthStateChanged.listen(callback);
}
#override
Future<void> writeUser(UserData user) async {
_firestore.collection("Users").add(user.toMap()).catchError((error) {
print(error);
});
}
}
If some of you know could you explain to me where/how I need to search in order to find this bug? Because how you can see I'm not using any read what so ever.
It's impossible to know for sure given that we don't understand all possible routes of access into your database, but you should be aware that use of the Firebase console will incur reads. If you leave the console open on a collection/document with busy write activity, the console will automatically read the changes that update the console's display. This is very often the source of unexpected reads.
Without full reproduction steps of exactly all the steps you're taking, there's no way to know for sure.
Firebase currently does not provide tools to track the origin of document reads. If you need to measure specific reads from your app, you will have to track that yourself somehow.
Related
Currently I am working in dev env on my local machine where I am storing passwords in plain text using MongoDB. I am using express-jwt along with jsonwebtoken for passing user data and authentication. I've researched bcryptjs and bcrypt and I would like to implement whichever is best for React, and Express, for passing hashed passwords to the database from the client. I have found resources for server side, but nothing for client side.
My question is then, what is the methodology for properly saving encrypted passwords on my server when they are passed from a client? How do I encrypt passwords client side then authenticate server side?
I have read some articles saying there is no need to encrypt client side due to ssl, but others say there is an absolute need to encrypt client side. What is the correct way, and how can I implement it on my React application?
Using Bcryptjs, Express and MongoDB:
There is no need to encrpyt client side, you can pass the password as plain text to the server using a post request (through a form usually).
Assuming you have a 'user' schema which looks similar to this:
const userSchema = new mongoose.Schema({
email:{type:String,required:true,unique:true},
password:{type:String,required:true}
},{collection:'users'}
const User= mongoose.model("User",userSchema);
)
On register/sign up in the server, where you handle the request you would hash the user's password like so:
app.post('/signup',async (req,res)=>{
// geting our data from frontend
const {email,password:plainTextPassword}=req.body;
// encrypting our password to store in database
const password = await bcrypt.hash(plainTextPassword,salt);
try {
// storing our user data into database
const response = await User.create({
email,
password
})
return res.redirect('/');
} catch (error) {
console.log(JSON.stringify(error));
if(error.code === 11000){
return res.send({status:'error',error:'email already exists'})
}
throw error
}
})
4.Upon login request (which will also be a post through a form o the client), you will compare the passwords using bcrpyt.compare() function, and if successful, assign a JWT to the user like so, this method assumes the token will be stored in the Cookies.
const verifyUserLogin = async (email,password)=>{
try {
const user = await User.findOne({email}).lean()
if(!user){
return {status:'error',error:'user not found'}
}
if(await bcrypt.compare(password,user.password)){
// creating a JWT token
token = jwt.sign({id:user._id,username:user.email,type:'user'},JWT_SECRET,{ expiresIn: '2h'})
return {status:'ok',data:token}
}
return {status:'error',error:'invalid password'}
} catch (error) {
console.log(error);
return {status:'error',error:'timed out'}
}
}
// login
app.post('/login',async(req,res)=>{
const {email,password}=req.body;
// we made a function to verify our user login
const response = await verifyUserLogin(email,password);
if(response.status==='ok'){
// storing our JWT web token as a cookie in our browser
res.cookie('token',token,{ maxAge: 2 * 60 * 60 * 1000, httpOnly: true }); // maxAge: 2 hours
res.redirect('/');
}else{
res.json(response);
}
})
I didn't address the front end, because it only includes basic POST request forms in react, there is no need for any special methods or processing on the client-side.
hope it helps.
edit, hashing client-side:
There is a debate about this, and in some protocols, it's even required to hash passwords on the client-side, in short, because SSL already encrypts everything that moves from client to server hashing on the client-side is pretty pointless and is not widely accepted today, even # big companies. Bottom line, the added security is neglectable, and is not worth the trouble and exposing hashing logic to the client side
You don't decrypt passwords. You ask the user for the password, then you hash it and compare it to the stored hash one you saved. If they're the same, then (assuming you have a secure hashing algorithm) the unencrypted versions must be the same also.
I have created windows flutter windows application now trying to save local data. I used shared_preferences plugin but it does not support windows applications so I found another package to save local data, sqflite_common_ffi plugin.
But don't know how to use it. I followed it's example and it worked but now want to separate it's all functionalities like I want to create a database in a method , and insert values in another method, get values in another method and delete them in another method.
CODE
import 'package:sqflite_common/sqlite_api.dart';
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
class Storages{
var databaseFactory = databaseFactoryFfi;
Database db;
createDatabase ()async{
// Init ffi loader if needed.
sqfliteFfiInit();
db = await databaseFactory.openDatabase(inMemoryDatabasePath);
await db.execute('''
CREATE TABLE Credential(
id INTEGER PRIMARY KEY,
token TEXT,
password TEXT
)
''');
}
saveToken(String token)async{
await db.insert('Credential', <String, dynamic>{'token': token});
}
savePassword(String password)async{
await db.insert('Credential', <String, dynamic>{'password': password});
}
getToken()async{
var result = await db.query('Credential');
print('Credential token database;$result');
// await db.close();
}
getPassword()async{
var result = await db.query('Credential');
print('Credential password database;$result');
}
}
then first I initialized database saved the password in database from login screen but when I restart my application
and I called getpassword method from initState of my splash screen.
Storages storage = new Storages();
#override
void initState() {
super.initState();
storage.getPassword();
}
and thrown me this exception in my terminal
Unhandled Exception: NoSuchMethodError: The method 'query' was called on null.
Receiver: null
Tried calling: query("Credential")
I would like to know how we can log the generated Refresh & AccessToken in IdentityServer 4.
Currently, we've got the custom implementation about the JwtAccessToken and we writes it + userId/name to the central logging system whenever it generates a new Access token. For Apis (we've more than 10), it always writes all incoming requests + JwtToken to the same logging system. So, we can easily trace what the user had done and see the logs/values at that particular time.
Now, we are going to replace that custom security implementation with IDSV4 and we couldn't find out a way to log the generated token in IDSV4.
We know that we can get the Access Token in .Net App by using await HttpContext.GetAccessTokenAsync(). But we don't want to manually send a log from all our apps (.Net, Spas, Apis (Client Credentials)) which are going to integrate with IDSV. We want to manage that AccessToken logging in a central place as we did before.
I looked at the IDSV4 sourcecode TokenEndpoint.cs Line120, LogTokens()
if (response.IdentityToken != null)
{
_logger.LogTrace("Identity token issued for {clientId} / {subjectId}: {token}", clientId, subjectId, response.IdentityToken);
}
if (response.RefreshToken != null)
{
_logger.LogTrace("Refresh token issued for {clientId} / {subjectId}: {token}", clientId, subjectId, response.RefreshToken);
}
if (response.AccessToken != null)
{
_logger.LogTrace("Access token issued for {clientId} / {subjectId}: {token}", clientId, subjectId, response.AccessToken);
}
Actually, they write the TraceLogs for the actual tokens. But we don't want to update the log level to Trace because it'll flood our logging system.
So, I would like to know whether it's possible to implement a feature to write a generated tokens to a log whenever IDSV4 issues an AccessToken. Is there anyway to intercept these tokens after the generation?
Or do we have to manually log AccessTokens whenever it's generated or refreshed in all our clients?
Update:
Thanks to sellotape for giving me an idea for DI. The following is the correct class to intercept the generated Token:
public class CustomTokenResponseGenerator : TokenResponseGenerator
{
public CustomTokenResponseGenerator(ISystemClock clock, ITokenService tokenService, IRefreshTokenService refreshTokenService, IResourceStore resources, IClientStore clients, ILogger<TokenResponseGenerator> logger) : base(clock, tokenService, refreshTokenService, resources, clients, logger)
{
}
public override async Task<TokenResponse> ProcessAsync(TokenRequestValidationResult request)
{
var result = await base.ProcessAsync(request);
// write custom loggings here
return result;
}
}
After that you can replace default class from IDSV4 with your custom class
services.Replace(ServiceDescriptor.Transient<ITokenResponseGenerator, CustomTokenResponseGenerator>());
There are many places to hook in for this; one is to create your own implementation of ITokenService by deriving from DefaultTokenService.
Override CreateAccessTokenAsync() and have it do:
Token result = await base.CreateAccessTokenAsync(request);
// Your logging goes here...
return result;
Swap in your version in your DI container at Startup (make sure it's after the default one has already been added):
services.Replace<ITokenService, MyTokenService>();
... and you should be ready.
As an aside, you should really log hashes of your tokens and not the tokens themselves. You can still match requests and actions to users based on the hash, but then at least nobody will be able to use the logging data to impersonate any of your users.
I'm using react with redux, and when the user logs in, I put the entire user object generated with the login in store. is it recommended to bring to the application any object user? const user = auth.signInWithEmailAndPassword(email,passwd)
export function* login(auth, action){
try{
const {email, password} = action.user
const user = yield auth.signInWithEmailAndPassword(email, passwd)
yield put(ActionCreator.signinSuccess(user))
}catch({message}){
yield put(ActionCreator.signinFailure("Dind't have possible do login"))
}
}
I use information like the token that comes in the user object to do authorization, the date of creation of the account and others .. so I keep all the object in the store of redux. Does this leave my system vulnerable?
Based on this answer (Is Redux secure?), is not a good idea to save sensitive data on the store.
If the data that you are saving will not compromise the application, it should be fine.
If not, then don't save on the store.
I am using Microsoft.Azure.ActiveDirectory.GraphClient version 2.1.1.0 to get groups that my user belongs to.
Method call is like this:
ActiveDirectoryClient activeDirectoryClient = new ActiveDirectoryClient(
new Uri(GraphUrl),
async () => await GetAppTokenAsync());
IEnumerable<string> groups = GetGroupsAsync(activeDirectoryClient, "currentUserObjectId").Result;
private static async Task<IEnumerable<string>> GetGroupsAsync(ActiveDirectoryClient activeDirectoryClient, string currentUserObjectId )
{
return await activeDirectoryClient.Users.GetByObjectId(currentUserObjectId).GetMemberGroupsAsync(true);
}
private static async Task<string> GetAppTokenAsync()
{
var authContext = new Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext(ServiceRoot);
var token = await authContext.AcquireTokenAsync(GraphUrl,new ClientCredential("clientId", "clientSecret"));
return token.AccessToken;
}
However the method hangs even though in Fiddler I see that the request has succedeed and contains correct groups.
My question is duplicate of Azure ActiveDirectory Graph API GraphClient not returning AD Groups. A workaround exists but not a explanation why the method does not work.
If indeed your ServiceRoot value is the same for your instantiation of ActiveDirectoryClient and for your call to AuthenticationContext, that could be the source of your problem.
ActiveDirectoryClient should be instantiated with https://graph.windows.net/
AuthenticationContext should be called with
https://login.microsoftonline.com/
Though that wouldn't manifest itself in the method hanging nor a successful request, that was the only change I had to make to your code for it to work for me, otherwise it would return with a Not Found error.
I've had similar issues with the Graph Api library when using the Result property, try changing your call to this:-
IEnumerable<string> groups = await GetGroupsAsync(activeDirectoryClient, "currentUserObjectId");