Skip to content

Authentication #144

Closed
Closed

Description

@mydreamdoctor

Are there any facilities built in to handle authentication, and if so, are there any examples demonstrating this?

Many Thanks.

Activity

  1. The-EDev commented on Jun 9, 2021

    @The-EDev
    Member

    Hi, Crow itself doesn't handle Authentication, Although it's worth mentioning that HTTP Authentication is built on top of Headers, cookies, and request bodies. All of which Crow does handle.

    You could find a good example for authentication handling (using Bearer Tokens) using Crow in my REST API called Forest (I use Gitlab for my personal projects). You can find the Authentication handling mainly in token.cpp, I just put Token::verifyToken at the top of any handler that requires authentication.
    You can ignore anything database related (or replace with your own implementation). the idea is to take what's after "Bearer" and compare it.

    For basic authentication. you can use the same method (replacing Bearer with Basic). You'll just need to decode the Base64 characters. and then handle the Username and Password separately.

  2. added
    questionIssue can be closed by providing information
    on Jun 9, 2021
  3. The-EDev commented on Jun 9, 2021

    @The-EDev
    Member

    Let me know if this answers your question. If it does not, feel free to ask further and I'll help out as much as I can.

  4. jessehl commented on Jun 10, 2021

    @jessehl

    @The-EDev: you wrote "I just put Token::verifyToken at the top of any handler that requires authentication.".

    Wouldn't you suggest the use of Middleware (as documented here) for automatically enforcing authentication on every route?

  5. The-EDev commented on Jun 10, 2021

    @The-EDev
    Member

    for automatically enforcing authentication on every route?

    This is specifically what I didn't want, I couldn't find a way to selectively run the middleware (see #45) which is why I resorted to putting Token::verifyToken only in the route I wanted Authentication to run in.

  6. ArthoPacini commented on Jun 14, 2021

    @ArthoPacini

    For Sessions you could create a Class called Session

        #ifndef _SESSION_HPP_
        #define _SESSION_HPP_
        
        #include <string>
        #include <chrono>
        
        class Session 
        {
        
            private:
            std::string mSessionHash;
            std::uint32_t mUserId;
            std::chrono::time_point<std::chrono::system_clock> created_at;
            std::chrono::seconds valid_time_in_seconds_from_created_time;
            
            public:
            Session(const std::string & session_hash, const std::uint32_t user_id, std::uint32_t valid_time_in_seconds)
            {
                created_at = std::chrono::system_clock::now();
                mSessionHash = session_hash;
                mUserId = user_id;
                valid_time_in_seconds_from_created_time = std::chrono::seconds(valid_time_in_seconds);
            }
            
            bool valid() const
            {
                return (created_at + valid_time_in_seconds_from_created_time) >= std::chrono::system_clock::now();
            }
            
            std::string hash() const
            {
                return mSessionHash;
            }
            
            std::uint32_t user_id() const
            {
                return mUserId;
            }
        
        
        };
        
        #endif // !_SESSION_HPP_

    that holds a session_hash, user id and valid time (so that the server can invalidate the session), and any other data you need.

    Then you may create a class called Sessions

        #ifndef _SESSIONS_HPP_
        #define _SESSIONS_HPP_
        
        #include <mutex>
        #include <unordered_map>
        #include <chrono>
        #include <utility>
        #include <tuple>
        #include <string>
        
        #include "Session.hpp"
        
        class Sessions final
        {
            private:
            {session_hash, SessionObject}
            std::unordered_map<std::string, Session> sessions;
            std::shared_mutex mSharedMutex;
            
            //{email, UserObject}
            std::unordered_map<std::string, User> users;
        
        //insert a session into the unordered_map
            std::string insert(const std::string & email, const std::uint32_t user_id, std::uint32_t valid_time_in_seconds = 900)
            {
                const std::string session_hash = some_random_secure_hash_function();
                std::lock_guard<std::shared_mutex> lock(mSharedMutex);
                sessions.insert({session_hash, Session(session_hash, user_id, valid_time_in_seconds)});
                return session_hash;
            }
        
            public:
        
            std::string create_session(const std::string & email, const std::string & password)
            {
                const auto user_find = users.find_user(email);
                if(user_find == users.end()) //could not find the user associated with this email
                    return std::string("");
        
               //User object need to have the 'validate_user' function to hash it and compare it
                const bool valid_user = user_find.second->validate_user(email, password);
                if(!valid_user)  //if not valid, no session hash for you
                    return std::string("");
        
                const auto & user = user_find.second;
                const std::uint32_t & user_id = user.id();
        
              //same password, create session and return session_hash
                return insert(email, user_id);
            }
        
        
            //try to find the session and validate it (calculate time)
            bool validate_session(const std::string & session_hash)
            {
                const auto session_find = sessions.find(session_hash);
        
                {
                    std::shared_lock<std::shared_mutex> lock(mSharedMutex);
                    if(session_find == sessions.end()) //session does not exists, not valid
                        return false;
                }
               //test if valid
                if(session_find->second.valid())
                    return true; //is valid!!!
        
                //if it's not valid, destroy session and returns false
                destroy_session(session_hash);
                return false;
            }
        
            void destroy_session(const std::string & session_hash)
            {
                std::lock_guard<std::shared_mutex> lock(mSharedMutex);
                sessions.erase(session_hash);
            }
        
        
        
        };
        
        #endif // !_SESSIONS_HPP_

    this should give you a nice start if you are willing to go with it. it's super fast and simple, you only need to sync it with a Database of your choice if you want it, and load from it afterwards

  7. The-EDev commented on Jun 14, 2021

    @The-EDev
    Member

    Closing this for lack of response. Please reopen if you feel the question was not answered properly.

  8. joelguittet commented on Mar 27, 2022

    @joelguittet
    Contributor

    @The-EDev hello,

    This page is referenced in the doc so now I have a class Session and a class Sessions and ... next ? what append ?

    It's not clear at all what should be done to have a simple server with basic authentication. Can you push a complete working example ?

    Something very simple with an helloworld displayed only if the user enter a static user+password for example.

    Thanks for the help

    PS : my context is : I have an existing small POC implementation in nodejs with ejs and basic auth with a login page, all together with bootstrap. node is convenient for the POC in my use case, but not for production. I'm looking for an equivalent and probably Crow is a good candidate but still need to confirm this scenario is possible.

  9. The-EDev commented on Mar 29, 2022

    @The-EDev
    Member

    @joelguittet Hi and sorry for being late to respond, things have been a little hectic with the 1.0 release.

    The scenario you're mentioning (a login page and basic auth) is very much possible with Crow. You probably won't need to use sessions and can instead give the client a token in a cookie and use that to authenticate.

    While I can make an example web page with what you need, but like I said the 1.0 release is taking much of my time. I'll still do my best to give you an example ASAP.
    I would also appreciate it if you could open a separate issue for making an HTTP Auth with a webpage example so that it doesn't get lost since this issue is closed.

  10. joelguittet commented on Mar 30, 2022

    @joelguittet
    Contributor

    Hello @The-EDev

    No problem, it's good to see the project is maintained and 1.0 is coming :-)
    Thanks a lot to confirm the scenario is possible, this make me confident to use the project and start prototyping something.
    No emergency on the example I understand you have a lot of work actually, I open a separated issue to keep trace of this request.

    Sincerely,
    Joel

  11. Akhil-Sharma-26 commented on Jan 17, 2025

    @Akhil-Sharma-26

    Hello,
    I did authentication in my project using the Access Token approach as given in the official docs, and it is working fine. But
    Is there any way I can use OAuth or OAuth2 in CrowCPP?
    The user will not need to remember another password and can use their existing Google or some other account to log in to the app.
    If someone has implemented this, can you please share any docs or code?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    questionIssue can be closed by providing information

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions