Open sebastienros opened 5 years ago
I like the simplicity of personal access tokens, but I also wonder how simple the OpenId flow could be or if it could be used behind the personal access tokens. @PinpointTownes
https://www.contentful.com/r/knowledgebase/personal-access-tokens/ https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/
Funny you suggest it, I actually talked with @PinpointTownes about simpler ways to authenticate in client apps, and he suggested PATs. And also that it should have it's custom module. So I assume we could have a page to create, list, revoke any PAT. And an authentication handler that would look for these in a header and impersonate the user.
Funny you suggest it, I actually talked with @PinpointTownes about simpler ways to authenticate in client apps, and he suggested PATs.
Well, I mentioned PATs (because it was the option corresponding to your criteria when we chatted), but I didn't say I suggested using them :sweat_smile:
Personally, I'd likely use the authorization code flow, by launching the default browser and running a temporary HTTP server to catch the callback authorization response or registering a custom system URI scheme (probably less handy for a CLI tooling).
The device code flow might also be a good option. If you want to see it in action, give the Azure CLI tooling a try. The downside is that it's still not officially standardized (and thus not supported by OpenIddict).
The Contentful CLI tooling uses the authorization code flow that opens a browser window and has you authenticate the app. I'm a little biased towards personal access tokens though since I need them to configure with Netlify to call back into Orchard when rebuilding my site.
https://github.com/contentful/contentful-cli
The only issue is how would the CLI be extended by specific modules. After all some features will only be available with some features.
So here's a crazy idea. Let's go all in on GraphQL. Since we can then do an introspection on the schema and dynamically build the list of supported CLI commands from the mutations that the modules provide.
So this is not something that has been done before and I'm only familiar with how we might do it as an npm package. Is dotnet global tool a must?
Here's some code that dynamically introspects a given schema from a remote URL and makes it available to call easily via the Delegate class. We would need to add in the schema to yargs code to be able to run graphql mutations as commands, but I think it should be doable.
(Ignore that it's an express app - was quickest way for me to test that the code worked)
const fetch = require('node-fetch')
const express = require('express');
const { Delegate } = require('graphql-binding/dist/Delegate')
const { HttpLink } = require('apollo-link-http')
const { setContext } = require('apollo-link-context');
const { introspectSchema, makeRemoteExecutableSchema, WrapQuery } = require('graphql-tools')
async function getExecutableSchema(link) {
const schema = await introspectSchema(link);
const executableSchema = makeRemoteExecutableSchema({
schema,
link,
});
return executableSchema
}
// Create the remote schema
const http = new HttpLink({ uri: 'http://orchardcoredomain/graphql', fetch });
const link = setContext((request, previousContext) => ({
headers: {
'Authorization': `Bearer 12345`,
}
})).concat(http);
(async () => {
const schema = await getExecutableSchema(link);
// Create the `before` function
const before = () => console.log(`Sending a request to the GraphQL API ...`)
const delegate = new Delegate({ schema, before })
const app = express();
app.get('/', (req, res) => {
// Testing Delegate
const args = { name: `My Tenant`, recipeName: `Blog` }
delegate.delegate(
`mutation`,
`createTenant`,
args
)
.then(createTenantResult => res.send(JSON.stringify(createTenantResult)).sendStatus(200))
.catch((e) => { console.error(e) })
})
const listener = app.listen(process.env.PORT, function() {
console.log('Your app is listening on port ' + listener.address().port);
});
})();
I totally agree with all the things you said.
The Contentful CLI tooling uses the authorization code flow that opens a browser window and has you authenticate the app.
Good to know! I'm not really surprised as it's certainly the most convenient approach (tho' it requires having a machine with a GUI, it's easier to use than the device code flow as you don't even have to copy/paste some user codes).
If we go with this approach, we can make it as simple as clicking on a "enable remote management" button in the admin UI (which would create an OpenID Connect client registration and some Orchard-specific scopes for the CLI tooling).
How does it work when we use a CLI like in Docker, could we use a different flow, and type the creds in the console app instead?
The password flow could be used for that, but if we want the "state of the art" option for headless machines (e.g a CLI executed via SSH or in a Docker container), we'll want to use the device code flow once it's standardized. It's what the Azure CLI tooling uses.
Interesting. Then how long is the authentication valid once the code has been registered on the web app? What does it store locally?
Then how long is the authentication valid once the code has been registered on the web app?
It's completely implementation-specific and depends on the lifespan of the access token returned by the authorization server. If a refresh token is returned, the CLI can retrieve new access tokens so nothing prevents us from having a never expiring authentication logic.
What does it store locally?
It can store the access token/refresh token or keep everything in-memory, depending on your choices.
I've started creating a node based CLI since the library support makes it a lot easier.
It's here currently but we can move it if you think it should be part of Orchard Core. https://github.com/jrestall/orchardcore-cli
It will generate a CLI based on the graphql endpoint you give it, so when pointing to http://api.githunt.com/graphql it will generate the following options:
orchardcore -h
Welcome to Orchard Core CLI
Usage: orchardcore <cmd> [args]
Commands:
orchardcore config Sets the CLI config
orchardcore submitRepository Submit a new repository, returns the
<repoFullName> new submission
orchardcore vote <repoFullName> Vote on a repository submission,
<type> returns the submission that was
voted on
orchardcore submitComment Comment on a repository, returns the
<repoFullName> <commentContent> new comment
Options:
-h, --help Show help [boolean]
-v, --version Show version number [boolean]
For more information, find the documentation at https://orchardcore.readthedocs.io/
It can show detailed documentation on specific mutations also:
orchardcore vote -h
Welcome to Orchard Core CLI
orchardcore.js vote <repoFullName> <type>
Vote on a repository submission, returns the submission that was voted on
Positionals:
repoFullName The full repository name from GitHub, e.g.
"apollostack/GitHunt-API" [string] [required]
type The type of vote - UP, DOWN, or CANCEL
[string] [required] [choices: "UP", "DOWN", "CANCEL"]
Options:
-h, --help Show help [boolean]
-v, --version Show version number [boolean]
I've added dotnet global tool support to the orchard-cli by using NodeServices. So now we support both nodejs and dotnet.
https://github.com/jrestall/orchardcore-cli/blob/master/Program.cs
To install & test:
dotnet pack
dotnet tool install --global --add-source ./nupkg orchardcore-cli
orchardcore config --host "https://api.githunt.com/graphql"
orchardcore -help
orchardcore vote -help
orchardcore vote myRepoName UP
It looks like a good idea, as I understand Orchard instance must have GraphQL and OpenId modules enabled before using Orchard CLI. Probably we can prepare some recipe which will provide "Orchard core CLI support" by enabling all necessary modules and probably adding some default ClientIds, Client Secrets for b2b and b2c scenarios.
For the record, OpenIddict 3.0 supports the device flow (which was standardized last year), so we'll be able to update the OpenID module to enable this flow.
@kevinchalet that's great, I love this flow as a user. Do you know any CLI that already uses it? I have never seen one so far. Only actual devices.
The Azure Powershell CLI supports it, tho' it's no longer the default flow (by default, it uses the code flow and starts the authorization dance in a webview, which is meh...). To see the device flow in action:
Connect-AzAccount -UseDeviceAuthentication
Related cli https://blog.repl.it/clui
It takes the graphql schema to build the UI/cli
We should have a dotnet global tool as the cli. This tool should work using REST APIs only. This means the Orchard Core application would need to be started already, which is normal and already supported by the dotnet sdk (we can build and start the application using the sdk, so anything would be done from the command line and from scripts).
The only issue is how would the CLI be extended by specific modules. After all some features will only be available with some features. And also authorization: should it be able to work remotely, how do we ensure the client has access to the instance (private key?).
Here is the Drupla CLI for reference: https://github.com/drush-ops/drush