Coder Social home page Coder Social logo

Comments (10)

Sammyjo20 avatar Sammyjo20 commented on September 18, 2024 1

Awesome, I think I might go for this then, here's a class style I've come up with:

class ForgeKeychain implements SaloonKeychain
{
    public function __construct(
        protected string $apiKey
     )
    {};
      
    public function default(): self
    {
        return new static(config('services.forge.key'));
    }
      
    public function toSaloon(SaloonRequest $request): void
    {
        // The keychain could have the helper methods like
        // addToken, addBasicAuth, addDigestAuth rather than the
        // request.
      
        $request->addHeader('Authentication', 'Bearer ' . $this->apiKey);
    }
}

I'm thinking of moving the authentication helpers like withToken to this keychain class in the form of a trait so it's a shortcut to add a bearer token rather than it being on the request and breaking the Saloon pattern

from saloon.

Sammyjo20 avatar Sammyjo20 commented on September 18, 2024

Thanks so much for writing this up @WalrusSoup. I think you have a really good point about how using "withToken" breaks out of Saloon's pattern of everything being wrapped in objects. I released it because I feel for some people might not be able to easily pass in a token. Maybe I should add it as an optional add-on trait rather than bolting it onto every request?

After seeing it from this angle I can see it actually adds a bit of confusion.

I like your DTO approach to this though. I have done this too on an API wrapper I built around Saloon with Oauth 2. Essentially I have a "Keychain" DTO which is returned after successfully generating authentication tokens, see here:

https://github.com/Sammyjo20/intelligent-office-php-sdk/blob/main/src/Concerns/AuthenticatesWithService.php

Here's the Keychain class:

https://github.com/Sammyjo20/intelligent-office-php-sdk/blob/main/src/Helpers/Keychain.php

Which looks very similar to what you are doing, but I would be quite happy to add this to do the docs with maybe a few tweaks inspired by my approach too.

from saloon.

WalrusSoup avatar WalrusSoup commented on September 18, 2024

I love that keychain approach (and the name, makes so much sense!).

One reason I used the approach inside the wrapper was it would act as a class property singleton. Requests new up a connector each time, which could be some load in unexpected places depending on where the dev gets their token.

In the case of Salesforce, Zoom, etc where requests are often paged it could mean unnecessarily contacting the database layer. While this is a small concern, it was enough to make me think optimization could be helpful.

I could see an approach where a Keychain feature built into actual library could be really helpful. I suppose the thing to ask then would be where does Saloon stop as far as scope? I think a keychain is probably as far as it should go.

Perhaps a class based approach built into the connector may be consistent with how Saloon is built out so far?

class MySeviceKeychain extends SaloonKeychain 
{
   // this can be in parent class
    protected static $instance;
   // could potentially have traits in here for defaults, useBearerHeader, etc. 
   
    // called if $instance was not built yet, user should write this function themselves
    public function buildKeychain() : MySeviceKeychain
    {
        // return whatever $instance should be? In the case of Authorization:bearer, just set return new self() with a token?
        return MyBackendFunction::loadCredentialsFromDatabaseAndDoStuff();
    }

    // user could code this themselves or this apply function can yield to the included trait (like Authorization: Bearer)
    public function apply(SaloonConnector $connector): void
    {
        // I should do my own logic here if i need something super custom
    }
}

Then over in the actual connector:

protected ?string $keychain = MySeviceKeychain::class;

Then I suppose if there is a keychain set in the connector, the connector can call build and then a built in apply or the a user supplied apply function.

Just a thought, though. In practice, not sure how maintainable this would be long term.

from saloon.

Sammyjo20 avatar Sammyjo20 commented on September 18, 2024

Glad you like the idea! I’ve now built two SDKs with Saloon using this approach, you authenticate the SDK with

‘$sdk->authenticate($keychain);’

Which loads it into the SDK to use later in your connector.

To be honest though, I see it being really useful as a way you can nicely transport credentials around the application, especially with your example where you might have different sources. What I did for one of my SDKs is I have a Laravel Model which has a method called “toKeychain” which returns its credentials as a SDK keychain that can be passed in. Maybe the same approach can be used for Saloon too, or like you’ve described - you can specify how a keychain is populated inside.

After you have created your keychain, you could pass it into your request or connector.

What would be nice is to let the user handle how the keychain should be processed, because different APIs might require different authentication/headers. Do you have any suggestions for this?

from saloon.

Sammyjo20 avatar Sammyjo20 commented on September 18, 2024

On a side note, I’m definitely interested in building a nice “Oauth” wrapper around Saloon that people can really easily use to connect to an Oauth 2 service. Quite a lot to process on that though so that’s for another day 😅

from saloon.

Sammyjo20 avatar Sammyjo20 commented on September 18, 2024

Ah, I just re-read your message and I like your implementation 👍

I agree with this is probably as far as I would go with Saloon, I don’t want it to be too opinionated but certainly make integrating with APIs easier. This could be a plugin that people add, but I’ll have a think.

Thanks for sparking the thought in me, this is a really cool discussion!

from saloon.

Sammyjo20 avatar Sammyjo20 commented on September 18, 2024

So here’s a first party idea from this discussion

  1. A user can define a Saloon “Keychain” which a class used to find credentials
  2. when a connector is booted up, the keychain’s “lookup” method is called (but can be blank)
  3. If the lookup method is not called the user is expected to pass in a keychain maybe like this:

Request::make()->authenticate($keychain)->send();

or

Request::make()->withKeychain($keychain)->send();

There could also be different types of keychain like:

  • BearerKeychain (will prefill the Authorizatoon header)
  • BasicAuthKeychain
  • Keychain (basic)

from saloon.

WalrusSoup avatar WalrusSoup commented on September 18, 2024

withKeychain would keep the testing consistent with what is in there now, so I like that approach. I also think the three options there would be good too for out of the box customizations without going overboard. As you said, the user would be responsible for handling the logic part of things as it pertains to their implementation and app.

I think the keychain approach also feels very Saloon. This library has a unique style that's very attractive code wise and I think the idea you're coming up with is in line with that.

from saloon.

Sammyjo20 avatar Sammyjo20 commented on September 18, 2024

I have implemented this in v9.0, however I'm having second thoughts. What if instead we just introduced a way to send requests easily from the connector rather than the request.

My initial thought of creating the request and then sending it was to keep it simple but sometimes you might want to create the connector so you can pass in authentication tokens into the connector.

I can see the keychain being useful if you want to authenticate individual requests for a particular user but I'm worried it's complicating Saloon a bit. I think if we kept both it would have to be described nicely in the docs as multiple ways you can authenticate.

What are your thoughts? I've placed this feature in an issue here: #46

from saloon.

WalrusSoup avatar WalrusSoup commented on September 18, 2024

Going to mark this as closed since we moved it into another issue 🙇

from saloon.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.