googleapis / google-auth-library-nodejs

🔑 Google Auth Library for Node.js
Apache License 2.0
1.73k stars 380 forks source link

Allow providing a `token` argument #677

Closed stephenplusplus closed 1 year ago

stephenplusplus commented 5 years ago

Thanks to @rac0316's research, when @google-cloud/common switched from google-auto-auth to this library, we lost the token option.

Could we bring it back?

// @JustinBeckwith

grant commented 5 years ago

This is a downstream (2x) issue that is blocking b/64476727

@tswast See: https://github.com/googleapis/nodejs-bigquery/issues/418

tswast commented 5 years ago

Is that a refresh token or an access token? Ideally we could take a refresh token and needed client secrets in order to support user credentials.

grant commented 5 years ago

Access token. Click the token link in the bug description.

JustinBeckwith commented 5 years ago

With the information I have now - I'm not in favor of a feature like this one. Access tokens expire. If we add an API that accepts an access token without the expiration and refresh token, we're just setting folks up for failure when it invariably times out.

@rac0316 may I ask - what's the use case where you specifically want to provide an access token? Why

Adding @broady for his thoughts here as well.

HuaRUAN commented 5 years ago

Running into the same situation. Downgraded to 1.3 works fine. The is a pivotal feature we use to give client a short time window of access to bq dataset.

broady commented 5 years ago

-1 to "token" but +1 to a function or something that provides a token (a promise, since it often involves a network request, e.g. to refresh), so that it can cache and refresh as it needs.

as @JustinBeckwith says, token expiry makes this dangerously useful.

HuaRUAN commented 5 years ago

Bq queries tend to be costly. How about we deliberately want to limit the bq usage? One token, no refresh.

broady commented 5 years ago

What do you do after the token has expired? In most cases, you don't actually want a static token, rather some token source that will always provide a valid token, refreshing if needed. (And caching still-valid tokens so you only refresh if you need to.)

In any case, a static token is handled pretty simply with a wrapper. In Go, this is called oauth2.StaticTokenSource

Roughly translated into broken JS:

const token = { token: "asdf", expiry: Date(...), ... }

let opts = {
  tokenSource: async () => {
    return token
  }
}
HuaRUAN commented 5 years ago

Not sure if it is common use case. We have a server check against user ldap and issue this token. It is meant for an one-off job. If expires user will request another token. We do not want the refresh happening automatically, which will effectively give user indefinite access time without been notified.

Tamirklein commented 5 years ago

I think returning the feature as it was in 1.3.0 is a good start and improving it can be done in the future branch. Not having this feature disable us from providing service to the client based on their token which is a big miss

komasoftware commented 5 years ago

I am sending my access_token from my web client to a cloud function to access the Google Drive API. I know that my access_token will expire, I do not want to ask my users for offline access to get a refresh token, but rather stick with minimal permissions.

MartinSahlen commented 5 years ago

Don't know if it applies, but managed to override this issue in bigquery by doing this: https://github.com/googleapis/nodejs-bigquery/issues/68#issuecomment-515673201

JSAURAJ commented 5 years ago

What is the conclusion? Add token or do nothing? I use access_token in older version, updating access_token manually with refresh token. What is the way to use the bigquery 4.2.1 library using the oauht2 client? I can't find any example.

Thanks.

tswast commented 5 years ago

I think Chris's tokenSource suggestion will be the most flexible (and consistent across languages). https://github.com/googleapis/google-auth-library-nodejs/issues/677#issuecomment-505646021

glebsts commented 3 years ago

Ran into same situation - our token is provided by hashicorp vault which app accesses by assuming some pod-embedded AWS roles, so we would like to set token manually and refresh also is our to manage. How can I put token or implement this tokenSource mentioned above?

Farrukh-Rana commented 3 years ago

We also have a usecase very similar to @glebsts usecase. We will be fetching temporary access tokens from hashicorp vault while the application is running and would ideally like to be able to pass (and set) access tokens to the underlying OAuthClient. Ofcourse building very vault specific refresh functionality within a common google auth library does not makes sense, so we would like to manage the lifecycle of the access tokens ourselves and simply want to be able to pass this access token to library client. For now, we have forked this library and will be building a hacky solution on top of it to provide the functionality of static access tokens. Would the maintainers of the library be interested if we opened up a (cleaned-up) PR for this static short-lived token functionality?

fpoon commented 2 years ago

Hi, we've come across this problem as well, while we were attempting to integrate BigQuery client with Vault managed tokens. As this issue seems to be stale, I want to share this snippet of static access token workaround for anyone else stumbling in here:

import {GetAccessTokenResponse, Headers} from "google-auth-library/build/src/auth/oauth2client"
import {GaxiosOptions, GaxiosPromise, GaxiosResponse} from "gaxios"
import {AuthClient} from "google-auth-library/build/src/auth/authclient"

class StaticAccessTokenAuthClient extends AuthClient {
    constructor(private readonly accessToken: string, protected quotaProjectId: string) {
        super()
    }

    getRequestHeaders(url?: string): Promise<Headers> {
        let headers = {
            Authorization: 'Bearer ' + this.accessToken
        } as Headers
        return Promise.resolve(
            this.addSharedMetadataHeaders(headers)
        )
    }

    // it seems that Service classes don't use this function, instead they make their own requests,
    // only incorporating headers from getRequestHeaders
    request<T>(opts: GaxiosOptions): GaxiosPromise<T> {
        return this.requestAsync<T>(opts)
    }

    private async requestAsync<T>(opts: GaxiosOptions): Promise<GaxiosResponse<T>> {
        opts.headers = {...opts.headers, ...await this.getRequestHeaders()}
        return Promise.resolve(this.transporter.request<T>(opts))
    }

    getAccessToken(): Promise<GetAccessTokenResponse> {
        return Promise.resolve({token: this.accessToken})
    }
}

let service = new BigQuery({projectId: "your-project-id"}) // or other client extending Service class from "@google-cloud/common"
let accessToken = getAccessToken() // your static access token
// acceptable types are restricted to JSONClient and Compute, but any AuthClient should do just fine
// @ts-ignore
service.authClient.cachedCredential = new StaticAccessTokenAuthClient(
    accessToken,
    service.projectId
)

Adjusting this code for Vault managed access tokens is fairly simple, just add function reading access token from Vault and pass it to getRequestHeaders method in place of static access token. There is a one caveat though, Service class seems to not use request method from auth client, so any solution based on replaying request with refreshed access token might fail. Instead, you need to keep track of token's TTL and refresh your access token within getRequestHeaders method.

I hope this issue will be resolved soon with some by-the-book solution (aforementioned clone of Golang's tokenSource looks cool), as authorization clients for other languages seem to already be capable of using access token provided by external services.

itsbrianburton commented 2 years ago

@JustinBeckwith @broady A specific Google-recommended use case is here: https://cloud.google.com/iam/docs/create-short-lived-credentials-direct Our microservices request a short-lived access token for each tenant and use that token to access their resources (bucket and database). No refresh tokens necessary.

We're dealing with this bug right now, and haven't found a working solution yet. Would love to give more visibility to this old issue.

cr3ative commented 2 years ago

As with @itsbrianburton, we just implemented this Google-recommended use case of short lived credentials, hit this problem, and had to implement a solution based on @fpoon's answer.

This was frustrating - it would be very welcome to see this functionality come back. I'm also in the "I create and refresh the token" category of user, I don't need this library to deal with refreshes.

danielbankhead commented 1 year ago

I think these are valid use cases, especially if customers know what they're doing. In some cases, a customer may only have an access token without a refresh token [1] [2]. Rather than creating a new client we can simply support passing an access_token to OAuth2Client and ensuring it works as expected.

Usage:

const authClient = new OAuth2Client({credentials: {access_token: ''}});
await authClient.request({url});
await authClient.getAccessToken();

// can pass to other libraries, like `google-cloud/storage`
const storage = new Storage({authClient});
await storage.getBuckets();

However, I don't think this should be a part of the Application Default Credentials flow - this should be used by folks that know exactly what they're up to.

Happy to close out the oldest open issue in the repo 😃

PR: #1652