does anyone have a working example of their "auth....
# singer-tap-development
i
does anyone have a working example of their "auth.py" that uses jwt authentication? I'm struggling with getting one working
👀 1
e
Hi @Ian OLeary! I don't think I actually have an example. What part are you struggling with?
i
So, I believe I got the "auth.py" returning a token, but still I'm getting an error "| WARNING | tap-jobdiva | Config validation failed: 'auth_token' is a required property; 'project_ids' is a required property", so do I need to declare somewhere in my meltano.yml that the 'auth_token' should be the cached authenticator property? Or am I misunderstanding. I manually set the "project_ids" to the same project id as "tap-myproject" in my meltano.yml. I'm running this by navigating to
taps\tap-mytap
, running
meltano install
, and then running
meltano invoke tap-mytap
Copy code
class MyTapAuthenticator(OAuthJWTAuthenticator):
    """Authenticator class for MyTap."""

    @classmethod
    def create_for_stream(
        cls,
        stream,  # noqa: ANN001
    ) -> MyTapAuthenticator:
        """Instantiate an authenticator for a specific Singer stream.

        Args:
            stream: The Singer stream instance.

        Returns:
            A new authenticator.
        """
        clientid = os.getenv("MYTAP_CLIENTID")
        username = os.getenv("MYTAP_USERNAME")
        password = os.getenv("MYTAP_PASSWORD")

        api_url = f"<https://api.mytap.com/api/authenticate?clientid={clientid}&username={username}&password={password}>"

        response = requests.get(api_url)

        if response.status_code ==200:
            auth_token = response.text
            return cls(
                stream=stream,
                auth_token=auth_token
            )
        else:
            raise Exception(f"Failed to authenticate with MyTap API: {response.text}")
my auth.py looks like this
and in isolation that returns an auth_token
e
What does your
tap.py
look like? Seems like you have required settings
auth_token
and
project_ids
that are missing?
i
Copy code
class TapMyTap(Tap):
    """MyTap tap class."""

    name = "tap-mytap"

    # TODO: Update this section with the actual config values you expect:
    config_jsonschema = th.PropertiesList(
        th.Property(
            "auth_token",
            th.StringType,
            required=True,
            secret=True,  # Flag config as protected.
            description="The token to authenticate against the API service",
        ),
        th.Property(
            "project_ids",
            th.ArrayType(th.StringType),
            required=True,
            description="Project IDs to replicate",
        ),
        th.Property(
            "start_date",
            th.DateTimeType,
            description="The earliest record date to sync",
        ),
        th.Property(
            "api_url",
            th.StringType,
            default="<https://api.mytap.com/api>",
            description="The url for the API service",
        ),
    ).to_dict()

    def discover_streams(self) -> list[streams.MyTapStream]:
        """Return a list of discovered streams.

        Returns:
            A list of discovered streams.
        """
        return [
            streams.NewUpdatedCandidateRecordsStream(self),
        ]
I haven't altered them in the tap.py yet. I've defined one stream in my streams.py (for now - for simplicity) which is shown here
e
seems like you want client_id, username and password as settings and pass those to your authenticator instance
i
so define them in the auth_token property itself?
what would that look like?
e
so define them in the auth_token property itself?
what would that look like?
rather as their own properties:
Copy code
config_jsonschema = th.PropertiesList(
        th.Property(
            "client_id",
            required=True,
             ...
        ),
        th.Property(
            "username",
            required=True,
             ...
        ),
        th.Property(
            "password",
            required=True,
             ...
        ),
and then in your auth class:
Copy code
clientid = self.config["client_id"]
username = self.config["username"]
password = self.config["password"]
i
Oh so I really don't need an auth_token property, do I?
e
No, not really
i
Copy code
class TapAuthenticator(OAuthJWTAuthenticator):
    """Authenticator class for Tap."""

    @classmethod
    def create_for_stream(
        self,
        cls,
        stream,  # noqa: ANN001
    ) -> TapAuthenticator:

        clientid = self.config["client_id"]
        username = self.config["username"]
        password = self.config["password"]

        api_url = f"<https://api.tap.com/api/authenticate?clientid={clientid}&username={username}&password={password}>"

        response = requests.get(api_url)

        if response.status_code ==200:
            auth_token = response.text
            return cls(
                stream=stream,
                auth_token=auth_token
            )
        else:
            raise Exception(f"Failed to authenticate with Tap API: {response.text}")
So here's what I've got now, Should I also be returning self in return cls()?
Also, since this returns the auth_token required for each stream, how do I then use the returned token in the "Authorization" in the headers of that particular stream?