idp.go (3669B)
1 // GoToSocial 2 // Copyright (C) GoToSocial Authors admin@gotosocial.org 3 // SPDX-License-Identifier: AGPL-3.0-or-later 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package oidc 19 20 import ( 21 "context" 22 "fmt" 23 24 "github.com/coreos/go-oidc/v3/oidc" 25 "github.com/superseriousbusiness/gotosocial/internal/config" 26 "github.com/superseriousbusiness/gotosocial/internal/gtserror" 27 "golang.org/x/oauth2" 28 ) 29 30 const ( 31 // CallbackPath is the API path for receiving callback tokens from external OIDC providers 32 CallbackPath = "/auth/callback" 33 ) 34 35 // IDP contains logic for parsing an OIDC access code into a set of claims by calling an external OIDC provider. 36 type IDP interface { 37 // HandleCallback accepts a context (pass the context from the http.Request), and an oauth2 code as returned from a successful 38 // login through an OIDC provider. It uses the code to request a token from the OIDC provider, which should contain an id_token 39 // with a set of claims. 40 // 41 // Note that this function *does not* verify state. That should be handled by the caller *before* this function is called. 42 HandleCallback(ctx context.Context, code string) (*Claims, gtserror.WithCode) 43 // AuthCodeURL returns the proper redirect URL for this IDP, for redirecting requesters to the correct OIDC endpoint. 44 AuthCodeURL(state string) string 45 } 46 47 type idp struct { 48 oauth2Config oauth2.Config 49 provider *oidc.Provider 50 oidcConf *oidc.Config 51 } 52 53 // NewIDP returns a new IDP configured with the given config. 54 func NewIDP(ctx context.Context) (IDP, error) { 55 // validate config fields 56 idpName := config.GetOIDCIdpName() 57 if idpName == "" { 58 return nil, fmt.Errorf("not set: IDPName") 59 } 60 61 issuer := config.GetOIDCIssuer() 62 if issuer == "" { 63 return nil, fmt.Errorf("not set: Issuer") 64 } 65 66 clientID := config.GetOIDCClientID() 67 if clientID == "" { 68 return nil, fmt.Errorf("not set: ClientID") 69 } 70 71 clientSecret := config.GetOIDCClientSecret() 72 if clientSecret == "" { 73 return nil, fmt.Errorf("not set: ClientSecret") 74 } 75 76 scopes := config.GetOIDCScopes() 77 if len(scopes) == 0 { 78 return nil, fmt.Errorf("not set: Scopes") 79 } 80 81 provider, err := oidc.NewProvider(ctx, issuer) 82 if err != nil { 83 return nil, err 84 } 85 86 protocol := config.GetProtocol() 87 host := config.GetHost() 88 89 oauth2Config := oauth2.Config{ 90 // client_id and client_secret of the client. 91 ClientID: clientID, 92 ClientSecret: clientSecret, 93 94 // The redirectURL. 95 RedirectURL: fmt.Sprintf("%s://%s%s", protocol, host, CallbackPath), 96 97 // Discovery returns the OAuth2 endpoints. 98 Endpoint: provider.Endpoint(), 99 100 // "openid" is a required scope for OpenID Connect flows. 101 // 102 // Other scopes, such as "groups" can be requested. 103 Scopes: scopes, 104 } 105 106 // create a config for verifier creation 107 oidcConf := &oidc.Config{ 108 ClientID: clientID, 109 } 110 111 if config.GetOIDCSkipVerification() { 112 oidcConf.SkipClientIDCheck = true 113 oidcConf.SkipExpiryCheck = true 114 oidcConf.SkipIssuerCheck = true 115 } 116 117 return &idp{ 118 oauth2Config: oauth2Config, 119 oidcConf: oidcConf, 120 provider: provider, 121 }, nil 122 }