Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add an otpProvider option to allow users to use a module to provide an OTP to semantic-release #234

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

MarshallOfSound
Copy link

This is an alternative take on #176 that allows folks to provide a generic "module" to load and provide an OTP which gives users a bit more freedom than "provide a URL".

An example usage of this is something like

{
  otpProvider: '@continuous-auth/semantic-client'
}

@MarshallOfSound MarshallOfSound force-pushed the otp-provider branch 2 times, most recently from ad45704 to 144fb63 Compare December 13, 2019 15:54
@pvdlg
Copy link
Member

pvdlg commented Dec 14, 2019

Both #176 and this PR are probably not the direction we are going to take to handle npm 2FA in semantic-release.
The reason is that solution defeat the purpose of 2FA and can allow to make a release without user entering the one time password.

I understand the backend solution can be implemented such that it would asks the user to enter the otp somewhere before sending it to semantic-release. But there is no guarantee that it would be implemented that way and would create situation that effectively circumvent the 2FA.

In addition, if you implement the backend such that is ask the user to enter the otp (so you don't circumvent 2FA), it would leave the semantic-release process hanging while the user enter the otp. If the user doesn't enter the otp, then the CI would end up killing the build on timeout and leave you with a partial release (git tag created, GitHub release created but no npm package published).

Instead I'm currently working on a service that would:

  • be targeted by npm publish (with npm publish --registry https://registry.airlockjs.com)
  • accept the publish right away without requesting the otp
  • store the package and publish parameters
  • send an email to the user

Then with a CLI or a web UI the user would be able to log in with their npm credential and see the stored packages. Then they can release them to the actual npm registry which would prompt them to enter their otp.

@MarshallOfSound
Copy link
Author

I understand the backend solution can be implemented such that it would asks the user to enter the otp somewhere before sending it to semantic-release. But there is no guarantee that it would be implemented that way and would create situation that effectively circumvent the 2FA.

I would argue that that is entirely on the user for doing that (if they make that choice, regardless of how bad it is). I don't think we should limit what the Good Actors can do just in case someone does a Bad Thing.

In addition, if you implement the backend such that is ask the user to enter the otp (so you don't circumvent 2FA), it would leave the semantic-release process hanging while the user enter the otp.

This is fine, we have been doing it like this for well into a year now and most CI providers have configurable timeouts.

If the user doesn't enter the otp, then the CI would end up killing the build on timeout and leave you with a partial release (git tag created, GitHub release created but no npm package published).

This is also a solvable problem, cleaning up a incomplete release is not difficult, delete a tag and empty github release and you're fine. We have our build timeout set to 30 minutes and we have never seen a timeout hit.

Then with a CLI or a web UI the user would be able to log in with their npm credential and see the stored packages. Then they can release them to the actual npm registry which would prompt them to enter their otp.

We also experimented with such a system, this results in a in-complete release until the user action of publishing to the actual registry is actually complete. So there is a github tag, release and changelog but no guarantee the npm publish actually went out (or will ever go out).

While I respect the direction you wish to take with your project 🙇 I would point out that all this is going to do is force us to maintain our electron/semantic-release-npm fork indefinitely which will only cause ecosystem fragmentation as other folks pick up and use the CFA system. Some kind of plugin system here to allow us to inject an OTP token into the CLI args here is all we need to avoid that.

@MarshallOfSound
Copy link
Author

For full context the CFA system was recently fully open sourced by the :electron: team at https://github.com/continuousauth

@pvdlg
Copy link
Member

pvdlg commented Dec 14, 2019

I would argue that that is entirely on the user for doing that (if they make that choice, regardless of how bad it is). I don't think we should limit what the Good Actors can do just in case someone does a Bad Thing.

Yes. However supporting a solution in a plugin from the @semantic-release organization might be seen as something we recommend and for which we will provide support. I don't want to give the impression that we recommend setting up a solution that ends up bypassing 2FA.

That said, you obviously put a lot of work into continuous-auth and I would like to find a way to make it work with semantic-release in an easy way. See below for a potential solution.

This is also a solvable problem, cleaning up a incomplete release is not difficult, delete a tag and empty github release and you're fine. We have our build timeout set to 30 minutes and we have never seen a timeout hit.

This is not trivial for many users. Per the issues and support requests we have, a lot of users do not have a clear enough understanding of how semantic-release work internally (using the tags to figure out the new commits) and don't know how to fix those partial releases.
In v16.0.0 we are using git notes to store additional info in the tags, so you would have to also modify the notes of the commit associated with the tag.

We also experimented with such a system, this results in a in-complete release until the user action of publishing to the actual registry is actually complete. So there is a github tag, release and changelog but no guarantee the npm publish actually went out (or will ever go out).

Yes. Both system have more or less the same problem, in your solution if the user doesn't enter the otp right away, in mine if they don't make the final release. However the solution I proposed has 2 advantages: the window of time for the user to enter the otp is a lot longer (as long as we keep the package stored) and failing to enter the otp doesn't result in situation technically problematic for semantic-release.
You do have a period of time were the release exists on GitHub but not on npm, but that doesn't create a technical issue anywhere.

While I respect the direction you wish to take with your project 🙇 I would point out that all this is going to do is force us to maintain our electron/semantic-release-npm fork indefinitely which will only cause ecosystem fragmentation as other folks pick up and use the CFA system. Some kind of plugin system here to allow us to inject an OTP token into the CLI args here is all we need to avoid that.

npm can read the otp from the environment variable NPM_CONFIG_OTP, so you could create a continuous-auth plugin that would implement the publish step like this:

import { getOtp } from '@continuous-auth/client';

async function publish(pluginConfig, context) {
  context.env.NPM_CONFIG_OTP = await getOtp();
}

You could even add a verifyConditions step to check for CFA_PROJECT_ID and CFA_SECRET.

Then configure this plugin to run just before @semantic-release/npm.

I didn't test but that should work because the env object is passed to the npm CLI. I can provide support to implement it.

This way we would have a clean way to support continuous-auth without giving users the possibility to use a similar solution that doesn't require manual input of the OTP.

Would that work for you?

@dominykas
Copy link
Contributor

dominykas commented Dec 14, 2019

I didn't test but that should work because the env object is passed to the npm CLI. I can provide support to implement it.

It might have been a while since I looked at it, but isn't context object cloned (rather than shared) between plugins? I think at some point I tried adding (or possibly modifying) values to it as a way to share information between different plugins and it didn't work. Would be great if you could confirm this (I might be able to test myself, of course, but not sure when exactly).

--

The downside of having a different plugin to set the OTP provider vs a config option is that you're then forced to define the plugin order in your release config. My gut is occasionally wrong, but I do feel uneasy each time I do that vs using the default plugins.

@pvdlg
Copy link
Member

pvdlg commented Dec 15, 2019

See semantic-release/semantic-release#1391. You can test with semantic-release on the branch plugins-env. If that allow to make a working plugin for continuous-auth I'll merge it.

@dominykas
Copy link
Contributor

Works for my use case: https://github.com/dominykas/test-things/blob/master/lib/poc-npm-set-otp.js

Instead I'm currently working on a service that would:

Is that the same thing mentioned here: https://twitter.com/BenjaminCoe/status/1208808072610758658?

There's also other alternatives proposed, incl. npm providing a "staging" area: nodejs/package-maintenance#282. It would be valuable if you could describe your proposal in there as well.

@pvdlg
Copy link
Member

pvdlg commented Dec 23, 2019

Is that the same thing mentioned here: https://twitter.com/BenjaminCoe/status/1208808072610758658?
There's also other alternatives proposed, incl. npm providing a "staging" area: nodejs/package-maintenance#282. It would be valuable if you could describe your proposal in there as well.

No it's something similar to nodejs/package-maintenance#282 (comment)

I describe it there #234 (comment)

But knowing that npm will implement something similar, I don't know if I'm going to finish mine. In any case if I release it, it would be there: https://github.com/airlockjs/airlockjs

@pvdlg
Copy link
Member

pvdlg commented Feb 7, 2020

@MarshallOfSound still interested of making https://github.com/continuousauth works with semantic-release?
What do you think of the solution proposed in #234 (comment) ?

Another solution would be to just add a continuousauth option to this plugin. Basically doing something similar to this PR but allowing only continuousauth rather than any package.

@MarshallOfSound
Copy link
Author

Hey @pvdlg sorry I've been super distracted by other things recently.

Looking through that proposed solution there would still be a race condition between the creation / fetch of the OTP and actually calling npm publish. Like if other plugins take too long the OTP could expire.

The best thing to do there is to get the OTP just before calling publish ourself.

Another solution would be to just add a continuousauth option to this plugin. Basically doing something similar to this PR but allowing only continuousauth rather than any package.

This sounds super optimal if you're ok with it

@pvdlg
Copy link
Member

pvdlg commented Feb 8, 2020

@MarshallOfSound I can work on the implementation by reusing some of the code in this PR if you are ok with it.
I would like to go a bit further and add a verify step. The goal is to avoid a situation where a misconfiguration would fail the build and let the user with a partial release.

Is there anything to verify other than CFA_SECRET and CFA_PROJECT_ID?
Do I have a way to verify those values are correct? Maybe an endpoint to check the authentication?

@MarshallOfSound
Copy link
Author

Is there anything to verify other than CFA_SECRET and CFA_PROJECT_ID?

Those are the only two environment variables.

Do I have a way to verify those values are correct? Maybe an endpoint to check the authentication?

There isn't an endpoint for that currently, though there can be quite easily. You'd just want an endpoint that takes those two variables as body / auth headers values and says "yes" or "no" right?

@pvdlg
Copy link
Member

pvdlg commented Feb 8, 2020

There isn't an endpoint for that currently, though there can be quite easily. You'd just want an endpoint that takes those two variables as body / auth headers values and says "yes" or "no" right?

Yes!

@MarshallOfSound
Copy link
Author

@pvdlg You can now use validateConfiguration --> https://github.com/continuousauth/node-client#validateconfiguration

@dominykas
Copy link
Contributor

Would it not make sense to also have a param for the host?

@MarshallOfSound
Copy link
Author

@dominykas technically there is, it's just undocumented and only used for testing atm. CFA_HOST is the variable, it's optional but would also be validated by the the validate function

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

Successfully merging this pull request may close these issues.

None yet

3 participants