-
Notifications
You must be signed in to change notification settings - Fork 278
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
appleid oauth works but probably not implemented the best
chore(deps): bump golang from 1.19.5-buster to 1.20.0-buster (#3949) Bumps golang from 1.19.5-buster to 1.20.0-buster. --- updated-dependencies: - dependency-name: golang dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Denis Mishin <dmishin@pomerium.com> implemented correct expiration, refresh and revoke chore(deps): bump golang from 1.19.5-buster to 1.20.0-buster (#3949) Bumps golang from 1.19.5-buster to 1.20.0-buster. --- updated-dependencies: - dependency-name: golang dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Denis Mishin <dmishin@pomerium.com> fixed lint issues and maybe ignored G101
- Loading branch information
Showing
3 changed files
with
208 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
// Package apple implements OpenID Connect for apple | ||
// | ||
// https://www.pomerium.com/docs/identity-providers/apple | ||
package apple | ||
|
||
import ( | ||
"context" | ||
"encoding/base64" | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"net/http" | ||
"net/url" | ||
"strings" | ||
"time" | ||
|
||
"golang.org/x/oauth2" | ||
|
||
"github.com/pomerium/pomerium/internal/httputil" | ||
"github.com/pomerium/pomerium/internal/identity/identity" | ||
"github.com/pomerium/pomerium/internal/identity/oauth" | ||
"github.com/pomerium/pomerium/internal/identity/oidc" | ||
"github.com/pomerium/pomerium/internal/urlutil" | ||
"github.com/pomerium/pomerium/internal/version" | ||
) | ||
|
||
// Name identifies the apple identity provider. | ||
const Name = "apple" | ||
|
||
var defaultScopes = []string{"name", "email"} | ||
var defaultAuthCodeOptions = map[string]string{ | ||
"response_mode": "form_post", | ||
} | ||
|
||
const ( | ||
defaultProviderURL = "https://appleid.apple.com" | ||
// ignore G101 linting issue as this is clearly a false positive | ||
tokenURL = "/auth/token" //nolint:golint | ||
authURL = "/auth/authorize" | ||
refreshDeadline = time.Minute * 60 | ||
revocationURL = "/auth/revoke" | ||
) | ||
|
||
// Provider is a Gitlab implementation of the Authenticator interface. | ||
type Provider struct { | ||
Oauth *oauth2.Config | ||
} | ||
|
||
// New instantiates an OpenID Connect (OIDC) provider for Apple. | ||
func New(ctx context.Context, o *oauth.Options) (*Provider, error) { | ||
p := Provider{} | ||
if o.ProviderURL == "" { | ||
o.ProviderURL = defaultProviderURL | ||
} | ||
|
||
if len(o.Scopes) == 0 { | ||
o.Scopes = defaultScopes | ||
} | ||
|
||
// Apple expects the AuthStyle to use Params instead of Headers | ||
// So we have to do out own oauth2 config | ||
p.Oauth = &oauth2.Config{ | ||
ClientID: o.ClientID, | ||
ClientSecret: o.ClientSecret, | ||
Scopes: o.Scopes, | ||
RedirectURL: o.RedirectURL.String(), | ||
Endpoint: oauth2.Endpoint{ | ||
AuthURL: urlutil.Join(o.ProviderURL, authURL), | ||
TokenURL: urlutil.Join(o.ProviderURL, tokenURL), | ||
AuthStyle: oauth2.AuthStyleInParams, | ||
}, | ||
} | ||
|
||
return &p, nil | ||
} | ||
|
||
// Name returns the provider name. | ||
func (p *Provider) Name() string { | ||
return Name | ||
} | ||
|
||
// GetSignInURL returns the url of the provider's OAuth 2.0 consent page | ||
// that asks for permissions for the required scopes explicitly. | ||
// | ||
// State is a token to protect the user from CSRF attacks. You must | ||
// always provide a non-empty string and validate that it matches the | ||
// the state query parameter on your redirect callback. | ||
// See http://tools.ietf.org/html/rfc6749#section-10.12 for more info. | ||
func (p *Provider) GetSignInURL(state string) (string, error) { | ||
opts := []oauth2.AuthCodeOption{} | ||
for k, v := range defaultAuthCodeOptions { | ||
opts = append(opts, oauth2.SetAuthURLParam(k, v)) | ||
} | ||
authURL := p.Oauth.AuthCodeURL(state, opts...) | ||
|
||
// Apple is very picky here and we need to use %20 instead of + | ||
// in order for all Apples device to correctly detect and use | ||
// native auth when available. | ||
// authURL = strings.Replace(authURL, "response_type=code", "response_type=code%20id_token", -1) | ||
authURL = strings.Replace(authURL, "scope=name+email", "scope=name%20email", -1) | ||
|
||
return authURL, nil | ||
} | ||
|
||
// Authenticate converts an authorization code returned from the identity | ||
// provider into a token which is then converted into a user session. | ||
func (p *Provider) Authenticate(ctx context.Context, code string, v identity.State) (*oauth2.Token, error) { | ||
oauth2Token, err := p.Oauth.Exchange(ctx, code) | ||
if err != nil { | ||
return nil, fmt.Errorf("identity/oidc: token exchange failed: %w", err) | ||
} | ||
|
||
err = p.UpdateUserInfo(ctx, oauth2Token, v) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return oauth2Token, nil | ||
} | ||
|
||
// LogOut is not implemented by github. | ||
func (p *Provider) LogOut() (*url.URL, error) { | ||
return nil, oidc.ErrSignoutNotImplemented | ||
} | ||
|
||
// Refresh is a no-op for github, because github sessions never expire. | ||
func (p *Provider) Refresh(ctx context.Context, t *oauth2.Token, v identity.State) (*oauth2.Token, error) { | ||
if t == nil { | ||
return nil, oidc.ErrMissingAccessToken | ||
} | ||
if t.RefreshToken == "" { | ||
return nil, oidc.ErrMissingRefreshToken | ||
} | ||
|
||
newToken, err := p.Oauth.TokenSource(ctx, t).Token() | ||
if err != nil { | ||
return nil, fmt.Errorf("identity/oidc: refresh failed: %w", err) | ||
} | ||
|
||
// Many identity providers _will not_ return `id_token` on refresh | ||
// https://github.com/FusionAuth/fusionauth-issues/issues/110#issuecomment-481526544 | ||
err = p.UpdateUserInfo(ctx, newToken, v) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return newToken, nil | ||
} | ||
|
||
// Revoke method will remove all the github grants the user | ||
// gave pomerium application during authorization. | ||
// | ||
// https://developer.github.com/v3/apps/oauth_applications/#delete-an-app-authorization | ||
func (p *Provider) Revoke(ctx context.Context, t *oauth2.Token) error { | ||
if t == nil { | ||
return oidc.ErrMissingAccessToken | ||
} | ||
|
||
params := url.Values{} | ||
params.Add("token", t.AccessToken) | ||
params.Add("token_type_hint", "access_token") | ||
// Some providers like okta / onelogin require "client authentication" | ||
// https://developer.okta.com/docs/reference/api/oidc/#client-secret | ||
// https://developers.onelogin.com/openid-connect/api/revoke-session | ||
params.Add("client_id", p.Oauth.ClientID) | ||
params.Add("client_secret", p.Oauth.ClientSecret) | ||
|
||
err := httputil.Do(ctx, http.MethodPost, revocationURL, version.UserAgent(), nil, params, nil) | ||
if err != nil && errors.Is(err, httputil.ErrTokenRevoked) { | ||
return fmt.Errorf("internal/oidc: unexpected revoke error: %w", err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// UpdateUserInfo will get the user information from github and also retrieve the user's team(s) | ||
// | ||
// https://developer.github.com/v3/users/#get-the-authenticated-user | ||
func (p *Provider) UpdateUserInfo(ctx context.Context, t *oauth2.Token, v interface{}) error { | ||
rawIDToken, ok := t.Extra("id_token").(string) | ||
if !ok { | ||
return oidc.ErrMissingIDToken | ||
} | ||
|
||
v.(identity.State).SetRawIDToken(rawIDToken) | ||
|
||
attributes := strings.Split(rawIDToken, ".")[1] | ||
|
||
rawDecodedText, err := base64.RawStdEncoding.DecodeString(attributes) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = json.Unmarshal(rawDecodedText, v) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters