9front - general discussion about 9front
 help / color / mirror / Atom feed
* [9front] Re: [9fans] OAuth2 in factotum
       [not found] <CALo7eEuXOfgr=2nj7A-rKNDejHdyBHWg98eJhvmLTneNaM5=Gw@mail.gmail.com>
@ 2021-08-22 20:16 ` ori
  2021-08-22 20:32   ` Demetrius Iatrakis
  2021-08-22 20:36   ` ori
  0 siblings, 2 replies; 4+ messages in thread
From: ori @ 2021-08-22 20:16 UTC (permalink / raw)
  To: 9fans, 9front

[-- Attachment #1: Type: text/plain, Size: 3933 bytes --]

Quoth Demetrius Iatrakis <demetrius.iatrakis@gmail.com>:
> This is a preview of OAuth2 support in factotum, as part of this year's GSoC:
> https://github.com/Mitsos101/plan9front/pull/1
> 
> Installation, on 9front:
> 
> git/clone https://github.com/Mitsos101/plan9front plan9front-oauth
> cd plan9front-oauth
> git/branch oauth
> bind sys/include /sys/include
> @{cd sys/src/libauth && mk install}
> @{cd sys/src/cmd/auth && mk install}
> @{cd sys/src/cmd/webfs && mk install}
> 
> This will replace your factotum.
> 
> Usage:
> 
> You need to obtain OAuth credentials from your issuer first. See, for
> example, Google's guide:
> https://developers.google.com/identity/protocols/oauth2.
> 
> % echo 'key proto=oauth issuer=https://accounts.google.com scope=email
> client_id=1234 !client_secret=5678' > /mnt/factotum/ctl
> % auth/oauth 'client_id=1234'
> go to https://google.com/device
> your code is ABCD-EFGH
> <after user consent is provided, the access token is printed>
> 
> auth_oauth is also available in libauth. Webfs uses it to implement
> the preoauth command.
> 
> Bugs:
> 
> This code is specific to 9front, as libjson is required and Plan 9's
> webfs doesn't support preoauth.
> 
> factotum uses the needkey RPC to display the verification URL and code
> to the user. This means that, for now, the needkey file must not be
> open so that fgui doesn't intercept it.
> 
> The module imports lots of code to support HTTP/1.0 so that the
> refresh token doesn't leave factotum's address space.
> 
> Only the device and refresh flows are supported. There is an
> implementation of the authorization code flow (tested on macOS) here:
> https://github.com/Mitsos101/plan9port/pull/1. However, it is not
> included in the module as there is no good browser to plumb the URL
> to.
> 
> Refresh tokens are not saved to persistent storage when factotum
> exits. The user must provide consent every time factotum is restarted.
> 

And, now that we have something working, I wrote
some code to use it. I wrote a patch to add oauth
support to upas/fs -- see attached:

To use the patch, I followed this kind of clunky
process:

	https://developers.google.com/identity/protocols/oauth2

I went to the 'credentials' section on the sidebar
and I created a key for a 'desktop application'; Then
I went to the 'oauth consent screen' and added my work
email account as a 'test user'.

I grabbed the keys, and on my unix box, went to
the patched oauth:

	% cd $HOME/src/plan9port/src/cmd/oauth

and generated a key using the full, browser based
auth flow:

	% python httpd.py
	% ./oauth https://accounts.google.com https://mail.google.com/ $clientkey $clientsecret
	key proto=oauth issuer=https://accounts.google.com client_id=72...

then edited the resulting output to include the appropriate
attributes, adding the attributes in >>...<< for upas/fs:

	key proto=oauth
		>>service=imap server=imap.gmail.com user=ori@pingthings.io<<
		issuer=https://accounts.google.com client_id=<id> 
		token_type=Bearer exptime=1629662303 scope=...

and then added that to factotum:

	echo key=... >/mnt/factotum/ctl

With that, upas/fs just worked with my work email:

	upas/fs -f /imaps/imap.gmail.com/ori@pingthings.io


Bugs: there are way too many steps. Unfortunately, the most
annoying one is generating and adding an oauth client key/secret,
and short of shipping a pregenerated one (is that a good idea?),
I don't think there's a solution.

Beyond that, 2 small bits of polish which I think we
can do:

	- Adding a '-t' flag to oauth (the way auth/rsa does)
	  to add type information to auth/oauth login would
	  make it more convenient to use: the output could
	  be stored directly rather than needing editing.
	- Adding a script that allows spawning a browser and
	  http listener on unix (or redirecting thigns through
	  to plan 9) would make it easier to drive the auth
	  process from plan 9.

Thanks for doing this work, Demetrius!

[-- Attachment #2: Type: text/plain, Size: 1855 bytes --]

diff bcfee7b54757eb64cade34e476cf0dba672832f6 uncommitted
--- a/sys/src/cmd/upas/fs/imap.c
+++ b/sys/src/cmd/upas/fs/imap.c
@@ -24,6 +24,7 @@
 	Cnolog	= 1<<0,
 	Ccram	= 1<<1,
 	Cntlm	= 1<<2,
+	Coauth	= 1<<3,
 
 	/* flags */
 	Fssl	= 1<<0,
@@ -151,7 +152,7 @@
 static void
 imap4cmd(Imap *imap, char *fmt, ...)
 {
-	char buf[256], *p;
+	char buf[1024], *p;
 	va_list va;
 
 	va_start(va, fmt);
@@ -430,6 +431,8 @@
 				imap->cap |= Ccram;
 			if(strcmp(p, "ntlm") == 0)
 				imap->cap |= Cntlm;
+			if(strcmp(p, "xoauth2") == 0)
+				imap->cap |= Coauth;
 		}else if(strcmp(t[i], "logindisabled") == 0)
 			imap->cap |= Cnolog;
 	}
@@ -733,6 +736,38 @@
 }
 
 static char*
+imap4oauth(Imap *imap)
+{
+	char *s, *auth, *enc;
+	int n;
+	OAuth *oa;
+
+	if(imap->user == nil)
+		return "user required for oauth";
+	oa = auth_getoauth(auth_getkey, "proto=oauth service=imap server=%q user=%q", imap->host, imap->user);
+	if(oa == nil)
+		return "cannot find IMAP oauth token";
+
+	imap->tag = 1;
+	if((auth = smprint("user=%s\x01auth=Bearer %s\x01\x01", imap->user, oa->access_token)) == nil)
+		sysfatal("smprint: %r");
+	if((enc = smprint("%[", auth) == nil)
+		sysfatal("smprint: %r");
+	imap4cmd(imap, "authenticate xoauth2 %s", enc);
+	free(auth);
+	free(enc);
+	free(oa);
+	s = imap4resp(imap);
+	if(isokay(s))
+		return nil;
+	imap4cmd(imap, "");
+	s = imap4resp(imap);
+	if(isokay(s))
+		return nil;
+	return s;
+}
+
+static char*
 imap4passwd(Imap *imap)
 {
 	char *s;
@@ -762,6 +797,8 @@
 		e = imap4cram(imap);
 	else if(imap->cap & Cntlm)
 		e = imap4ntlm(imap);
+	else if(imap->cap & Coauth)
+		e = imap4oauth(imap);
 	else
 		e = imap4passwd(imap);
 	if(e)
@@ -1165,6 +1202,7 @@
 	imap->host = f[2];
 	if(strstr(imap->host, "gmail.com"))
 		imap->flags |= Fgmail;
+	imap->flags |= Fdebug;
 	imap->refreshtime = 60;
 	if(nf < 4)
 		imap->user = nil;

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [9front] Re: [9fans] OAuth2 in factotum
  2021-08-22 20:16 ` [9front] Re: [9fans] OAuth2 in factotum ori
@ 2021-08-22 20:32   ` Demetrius Iatrakis
  2021-08-22 20:38     ` ori
  2021-08-22 20:36   ` ori
  1 sibling, 1 reply; 4+ messages in thread
From: Demetrius Iatrakis @ 2021-08-22 20:32 UTC (permalink / raw)
  To: 9fans; +Cc: 9front

Thank you for your work.

Don't forget to add

type is text
dst is oauth
plumb to oauth

to your plumber's rules file to create the oauth port.

I have also added the authorization code flow to the factotum module
(can be enabled by adding `flow=auth` to the key), but it requires the
user to import the plan9port plumber into factotum's namespace. I am
unsure what the best way to do this is. Plan9port provides import(4)
which relies on SSH, but 9front does not provide an SSH server.

-- 
Demetrius

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [9front] Re: [9fans] OAuth2 in factotum
  2021-08-22 20:16 ` [9front] Re: [9fans] OAuth2 in factotum ori
  2021-08-22 20:32   ` Demetrius Iatrakis
@ 2021-08-22 20:36   ` ori
  1 sibling, 0 replies; 4+ messages in thread
From: ori @ 2021-08-22 20:36 UTC (permalink / raw)
  To: 9fans, 9front

[-- Attachment #1: Type: text/plain, Size: 4372 bytes --]

Quoth ori@eigenstate.org:
> Quoth Demetrius Iatrakis <demetrius.iatrakis@gmail.com>:
> > This is a preview of OAuth2 support in factotum, as part of this year's GSoC:
> > https://github.com/Mitsos101/plan9front/pull/1
> > 
> > Installation, on 9front:
> > 
> > git/clone https://github.com/Mitsos101/plan9front plan9front-oauth
> > cd plan9front-oauth
> > git/branch oauth
> > bind sys/include /sys/include
> > @{cd sys/src/libauth && mk install}
> > @{cd sys/src/cmd/auth && mk install}
> > @{cd sys/src/cmd/webfs && mk install}
> > 
> > This will replace your factotum.
> > 
> > Usage:
> > 
> > You need to obtain OAuth credentials from your issuer first. See, for
> > example, Google's guide:
> > https://developers.google.com/identity/protocols/oauth2.
> > 
> > % echo 'key proto=oauth issuer=https://accounts.google.com scope=email
> > client_id=1234 !client_secret=5678' > /mnt/factotum/ctl
> > % auth/oauth 'client_id=1234'
> > go to https://google.com/device
> > your code is ABCD-EFGH
> > <after user consent is provided, the access token is printed>
> > 
> > auth_oauth is also available in libauth. Webfs uses it to implement
> > the preoauth command.
> > 
> > Bugs:
> > 
> > This code is specific to 9front, as libjson is required and Plan 9's
> > webfs doesn't support preoauth.
> > 
> > factotum uses the needkey RPC to display the verification URL and code
> > to the user. This means that, for now, the needkey file must not be
> > open so that fgui doesn't intercept it.
> > 
> > The module imports lots of code to support HTTP/1.0 so that the
> > refresh token doesn't leave factotum's address space.
> > 
> > Only the device and refresh flows are supported. There is an
> > implementation of the authorization code flow (tested on macOS) here:
> > https://github.com/Mitsos101/plan9port/pull/1. However, it is not
> > included in the module as there is no good browser to plumb the URL
> > to.
> > 
> > Refresh tokens are not saved to persistent storage when factotum
> > exits. The user must provide consent every time factotum is restarted.
> > 
> 
> And, now that we have something working, I wrote
> some code to use it. I wrote a patch to add oauth
> support to upas/fs -- see attached:
> 
> To use the patch, I followed this kind of clunky
> process:
> 
> 	https://developers.google.com/identity/protocols/oauth2
> 
> I went to the 'credentials' section on the sidebar
> and I created a key for a 'desktop application'; Then
> I went to the 'oauth consent screen' and added my work
> email account as a 'test user'.
> 
> I grabbed the keys, and on my unix box, went to
> the patched oauth:
> 
> 	% cd $HOME/src/plan9port/src/cmd/oauth
> 
> and generated a key using the full, browser based
> auth flow:
> 
> 	% python httpd.py
> 	% ./oauth https://accounts.google.com https://mail.google.com/ $clientkey $clientsecret
> 	key proto=oauth issuer=https://accounts.google.com client_id=72...
> 
> then edited the resulting output to include the appropriate
> attributes, adding the attributes in >>...<< for upas/fs:
> 
> 	key proto=oauth
> 		>>service=imap server=imap.gmail.com user=ori@pingthings.io<<
> 		issuer=https://accounts.google.com client_id=<id> 
> 		token_type=Bearer exptime=1629662303 scope=...
> 
> and then added that to factotum:
> 
> 	echo key=... >/mnt/factotum/ctl
> 
> With that, upas/fs just worked with my work email:
> 
> 	upas/fs -f /imaps/imap.gmail.com/ori@pingthings.io
> 
> 
> Bugs: there are way too many steps. Unfortunately, the most
> annoying one is generating and adding an oauth client key/secret,
> and short of shipping a pregenerated one (is that a good idea?),
> I don't think there's a solution.
> 
> Beyond that, 2 small bits of polish which I think we
> can do:
> 
> 	- Adding a '-t' flag to oauth (the way auth/rsa does)
> 	  to add type information to auth/oauth login would
> 	  make it more convenient to use: the output could
> 	  be stored directly rather than needing editing.
> 	- Adding a script that allows spawning a browser and
> 	  http listener on unix (or redirecting thigns through
> 	  to plan 9) would make it easier to drive the auth
> 	  process from plan 9.
> 
> Thanks for doing this work, Demetrius!

Oops, realized that I'd left this line in the patch:

+	imap->flags |= Fdebug;

you'll really want to delete that, or you end up with
a *TON* of debug spew.

Updated patch attached.

[-- Attachment #2: Type: text/plain, Size: 1663 bytes --]

diff bcfee7b54757eb64cade34e476cf0dba672832f6 uncommitted
--- a/sys/src/cmd/upas/fs/imap.c
+++ b/sys/src/cmd/upas/fs/imap.c
@@ -24,6 +24,7 @@
 	Cnolog	= 1<<0,
 	Ccram	= 1<<1,
 	Cntlm	= 1<<2,
+	Coauth	= 1<<3,
 
 	/* flags */
 	Fssl	= 1<<0,
@@ -151,7 +152,7 @@
 static void
 imap4cmd(Imap *imap, char *fmt, ...)
 {
-	char buf[256], *p;
+	char buf[1024], *p;
 	va_list va;
 
 	va_start(va, fmt);
@@ -430,6 +431,8 @@
 				imap->cap |= Ccram;
 			if(strcmp(p, "ntlm") == 0)
 				imap->cap |= Cntlm;
+			if(strcmp(p, "xoauth2") == 0)
+				imap->cap |= Coauth;
 		}else if(strcmp(t[i], "logindisabled") == 0)
 			imap->cap |= Cnolog;
 	}
@@ -733,6 +736,38 @@
 }
 
 static char*
+imap4oauth(Imap *imap)
+{
+	char *s, *auth, *enc;
+	int n;
+	OAuth *oa;
+
+	if(imap->user == nil)
+		return "user required for oauth";
+	oa = auth_getoauth(auth_getkey, "proto=oauth service=imap server=%q user=%q", imap->host, imap->user);
+	if(oa == nil)
+		return "cannot find IMAP oauth token";
+
+	imap->tag = 1;
+	if((auth = smprint("user=%s\x01auth=Bearer %s\x01\x01", imap->user, oa->access_token)) == nil)
+		sysfatal("smprint: %r");
+	if((enc = smprint("%[", auth) == nil)
+		sysfatal("smprint: %r");
+	imap4cmd(imap, "authenticate xoauth2 %s", enc);
+	free(auth);
+	free(enc);
+	free(oa);
+	s = imap4resp(imap);
+	if(isokay(s))
+		return nil;
+	imap4cmd(imap, "");
+	s = imap4resp(imap);
+	if(isokay(s))
+		return nil;
+	return s;
+}
+
+static char*
 imap4passwd(Imap *imap)
 {
 	char *s;
@@ -762,6 +797,8 @@
 		e = imap4cram(imap);
 	else if(imap->cap & Cntlm)
 		e = imap4ntlm(imap);
+	else if(imap->cap & Coauth)
+		e = imap4oauth(imap);
 	else
 		e = imap4passwd(imap);
 	if(e)

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [9front] Re: [9fans] OAuth2 in factotum
  2021-08-22 20:32   ` Demetrius Iatrakis
@ 2021-08-22 20:38     ` ori
  0 siblings, 0 replies; 4+ messages in thread
From: ori @ 2021-08-22 20:38 UTC (permalink / raw)
  To: 9fans; +Cc: 9front

Quoth Demetrius Iatrakis <demetrius.iatrakis@gmail.com>:
> 
> type is text
> dst is oauth
> plumb to oauth
> 
> to your plumber's rules file to create the oauth port.

Ah -- right. This is in the plan9port side, and
it's used to send the oauth verification request
to the browser.

(iirc, it's not currently used for anything on the
plan 9 side, though it wil be once we figure out
how to glue together auth flows to either vmx or
some unix browser)


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2021-08-22 20:50 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <CALo7eEuXOfgr=2nj7A-rKNDejHdyBHWg98eJhvmLTneNaM5=Gw@mail.gmail.com>
2021-08-22 20:16 ` [9front] Re: [9fans] OAuth2 in factotum ori
2021-08-22 20:32   ` Demetrius Iatrakis
2021-08-22 20:38     ` ori
2021-08-22 20:36   ` ori

9front - general discussion about 9front

This inbox may be cloned and mirrored by anyone:

	git clone --mirror https://inbox.vuxu.org/9front

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V1 9front 9front/ https://inbox.vuxu.org/9front \
		9front@9front.org
	public-inbox-index 9front

Example config snippet for mirrors.
Newsgroup available over NNTP:
	nntp://inbox.vuxu.org/vuxu.archive.9front


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git