Currently there is no way to fetch remote modules from private GitHub repositories without exposing your access token.
For example you could do the following currently:
import * as foo from "https://[email protected]/private_org/private_repo/master/foo.ts";
But you could easily "leak" your access token if you then checked that code in and pushed it to a public repo.
GitHub (and I assume other services) all the token to be passed as an authorisation header:
Authorization: token $TOKEN
If this was somehow passed on the command line, it would become easier to secure.
This is a show stopper for Deno at many companies who need to use private modules and have a policy of not embedding auth tokens into their software.
What we need is a decent proposal of what the UI should be for passing it. It should be easy to specify multiple tokens for different hosts, and only when connecting to those hosts, will the header be sent. Maybe [TOKEN]@[HOST] would work? So on the CLI it something like:
$ deno run --auth-token [TOKEN]@raw.githubusercontent.com https://raw.githubusercontent.com/private_org/repo/master/main.ts
Or:
$ deno run --auth-token=[TOKEN]@raw.githubusercontent.com,[TOKEN]@example.com https://raw.githubusercontent.com/private_org/repo/master/main.ts
I like it.
Some points:
We have avoided meta data files. I would loath to add one for this. Using an environment variable like DENO_AUTH_TOKEN with the same comma separated format should address the length problem. As far as other auth, personal opinion is we keep it to tokens. Basic Auth encourages bad security practices and I am not aware of something professional grade that doesn't support tokens for things like this. It could be an enhancement down the road if there is a legitimate use case.
I like the way node was going with something like a .npmrc file. Think also about how to build the code in a CI server. Maybe there's a way to put inside the import map file.
{
"imports": {
...
},
"authentication": {
"github:denoland": "${GITHUB_ACCESS_TOKEN}",
"github:denoland/deno": "${GITHUB_ACCESS_TOKEN_2}"
}
}
Without that feature Deno in unusable for us, because all our libraries are in private GitHub repositories.
This is a brilliant idea and would really assist when modules are hosted on private registries. I really like the import map file suggestion as to where to place this config, as far as I can tell, the only problem is, this is nonstandard AFAIK from here. I suggest that any programmatic API will also include authentication tokens and then the community can build there own tooling around it.
a way to put inside the import map file
Import maps are based on a draft standard. Modifying that will end up in 馃槩. There is this issue #3179 for that, but I don't think it will go anywhere any time soon. The feature can easily ship without solving that problem too.
For now everything in this thread is around GitHub, I would strongly suggest to also support other repositories like Azure DevOps, BitBucket, etc.
@maciejmaciejewski they support auth tokens as well...
GitHub didn't invent the standard.
@kitsonk What I meant is all of those can handle the Authorization header in a different way - some can use Bearer token, some other Basic one so just want to make sure that all the use cases are covered.
We could use providers for authentication like github:denoland or azure:denoland. Deno needs to recognize that an import from e.g. https://raw.githubusercontent.com/private_org/private_repo/master/foo.ts is handled by the corresponding provider.
But where do you end with adding 'providers'? Now there's a magic list of 'providers' who are 'important enough' ? This doesn't seem like a scalable solution. Also +1 for avoiding more dotfiles, most javascript repos have more dotfiles than codefiles these days :crying_cat_face:
We need to get it implemented on the command line and with environment variables first, and iterate from there. We don't need to solve every problem at the start.
One issue I see with the [TOKEN]@[HOST] solution is needing to provide tokens for multiple private github projects at the same host. I don't have this issue right now but I cam imagine someone trying to provide a token for two separate private repositories both on the same host (github). The HOST would need to include some path components to avoid conflict?
@pomke the tokens are tied to users, not repos... it would be a limitation that the same user has to have read access to all the repos. That is a limitation that seems reasonable.
As most providers use the Authorization header, one option is to simply provide the raw header value as an environment variable i.e. the env var would include the Bearer or Basic portion of the string.
For GitHub it鈥檚 enough to have an environment variable. Deno just needs to Respekt it.
I'd suggest something like this:
json
"importOptions": {
"headers": {
"authorization": {
"github.com/denoland/deno": "Bearer ${DENO_TOKEN}",
}
}
}
authorization can be replaced with any custom header name. The keys inside authorization could be tested against each import URL and the first partial substring match should be used. In the same vein, one could implement TLS client certificates for import:
json
"importOptions": {
"clientCertificate": {
"github.com/denoland/deno": "${CLIENT_CERT}",
},
"clientCertificateKey": {
"github.com/denoland/deno": "${CLIENT_CERT_KEY}",
}
}
The data could also be expose to JS using import.meta.options.
My suggestion is to add -n or --netrc (or similarly named) option, which will handle credentials placed in ~/.netrc, just like curl:
Usage: curl [options...] <url>
-n, --netrc Must read .netrc for user name and password
--netrc-file <filename> Specify FILE for netrc
In ~/.netrc you'll have this:
machine raw.githubusercontent.com
login YOUR_TOKEN
password x-oauth-basic
(YOUR_TOKEN is a personal Github token with "Full control of private repositories")
Now you can easily:
curl -n https://raw.githubusercontent.com/private_org/private_repo/master/foo.ts
Wouldn't work if you won't pass -n option though:
curl https://raw.githubusercontent.com/private_org/private_repo/master/foo.ts # will return 404
Just try it with Github and curl, it's very neat.
Would be cool if it worked the same way in Deno.
Thanks for suggestion @JerryGreen but we're very careful about introducing configuration files and we don't pick them up automatically. I think this problem problem should be solved as part of single "metadata" file:
@bartlomieju We should not put this in the metadata file, because metadata file will be shared between multiple people. This config should be per user.
I do like the idea of netrc. It is relatively standard, and existing parsers for it exist in Rust - we wouldnt have to invent anything new. We wouldn't pick up the file automatically though, (instead you have to explicitly specify the path to it with a flag, as environment variable, or as a key in the metadata file).
Keep in mind .netrc will probably not be able to support other forms of authorization, like client certificates. Also, storing passwords in plaintext on the disk may be a security risk.
Most helpful comment
As most providers use the
Authorizationheader, one option is to simply provide the raw header value as an environment variable i.e. the env var would include theBearerorBasicportion of the string.