()
| 131 | } |
| 132 | |
| 133 | func (h *handler) handleOIDCCallback() error { |
| 134 | callbackError := h.getQuery(requestParamError) |
| 135 | if callbackError != "" { |
| 136 | errorDescription := h.getQuery(requestParamErrorDesc) |
| 137 | return base.HTTPErrorf(http.StatusUnauthorized, "oidc callback received an error: %v", errorDescription) |
| 138 | } |
| 139 | |
| 140 | code := h.getQuery(requestParamCode) |
| 141 | if code == "" { |
| 142 | return base.HTTPErrorf(http.StatusBadRequest, "Code must be present on oidc callback") |
| 143 | } |
| 144 | |
| 145 | providerName := h.getQuery(requestParamProvider) |
| 146 | provider, err := h.getOIDCProvider(providerName) |
| 147 | if err != nil || provider == nil { |
| 148 | return base.HTTPErrorf(http.StatusBadRequest, "Unable to identify provider for callback request") |
| 149 | } |
| 150 | |
| 151 | // Validate state parameter to prevent cross-site request forgery (CSRF) when callback state is enabled. |
| 152 | if !provider.DisableCallbackState { |
| 153 | stateCookie, err := h.rq.Cookie(stateCookieName) |
| 154 | |
| 155 | if err == http.ErrNoCookie || stateCookie == nil { |
| 156 | return ErrNoStateCookie |
| 157 | } |
| 158 | |
| 159 | if err != nil { |
| 160 | base.WarnfCtx(h.ctx(), "Unexpected error attempting to read OIDC state cookie: %v", err) |
| 161 | return ErrReadStateCookie |
| 162 | } |
| 163 | |
| 164 | stateParam := h.rq.URL.Query().Get(requestParamState) |
| 165 | if stateParam != stateCookie.Value { |
| 166 | return ErrStateMismatch |
| 167 | } |
| 168 | |
| 169 | // Delete the state cookie on successful validation. |
| 170 | stateCookie = h.makeStateCookie("", -1) |
| 171 | http.SetCookie(h.response, stateCookie) |
| 172 | } |
| 173 | |
| 174 | client, err := provider.GetClient(h.ctx(), h.getOIDCCallbackURL) |
| 175 | if err != nil { |
| 176 | return fmt.Errorf("OIDC initialization error: %w", err) |
| 177 | } |
| 178 | |
| 179 | // Converts the authorization code into a token. |
| 180 | context := auth.GetOIDCClientContext(provider.InsecureSkipVerify) |
| 181 | token, err := client.Config().Exchange(context, code) |
| 182 | if err != nil { |
| 183 | return base.HTTPErrorf(http.StatusInternalServerError, "Failed to exchange token: %s", err.Error()) |
| 184 | } |
| 185 | |
| 186 | rawIDToken, ok := token.Extra("id_token").(string) |
| 187 | if !ok { |
| 188 | return base.HTTPErrorf(http.StatusInternalServerError, "No id_token field in oauth2 token.") |
| 189 | } |
| 190 | base.InfofCtx(h.ctx(), base.KeyAuth, "Obtained token from Authorization Server: %v", rawIDToken) |
nothing calls this directly
no test coverage detected