Beispiel #1
0
func (h *Handler) AuthorizeHandler(w http.ResponseWriter, r *http.Request) {
	resp := h.server.NewResponse()
	defer resp.Close()
	if ar := h.server.HandleAuthorizeRequest(resp, r); ar != nil {
		// For now, a provider must be given.
		// TODO there should be a fallback provider which is a redirect to the login endpoint. This should be configurable by env var.
		// Let's see if this is a valid provider. If not, return an error.
		provider, err := h.Providers.Find(r.URL.Query().Get("provider"))
		if err != nil {
			http.Error(w, fmt.Sprintf(`Provider "%s" not known.`, err), http.StatusBadRequest)
			return
		}

		// This could be made configurable with `connection.GetCodeKeyName()`
		code := r.URL.Query().Get("access_code")
		if code == "" {
			// If no code was given we have to initiate the provider's authorization workflow
			url := provider.GetAuthCodeURL(ar)
			http.Redirect(w, r, url, http.StatusFound)
			return
		}

		// Create a session by exchanging the code for the auth code
		connection, err := provider.Exchange(code)
		if err != nil {
			http.Error(w, fmt.Sprintf("Could not exchange access code: %s", err), http.StatusUnauthorized)
			return
		}

		subject := connection.GetRemoteSubject()
		user, err := h.Connections.FindByRemoteSubject(provider.GetID(), subject)
		if err == account.ErrNotFound {
			// The subject is not linked to any account.
			http.Error(w, "Provided token is not linked to any existing account.", http.StatusUnauthorized)
			return
		} else if err != nil {
			// Something else went wrong
			http.Error(w, fmt.Sprintf("Could assert subject claim: %s", err), http.StatusInternalServerError)
			return
		}

		ar.UserData = jwt.NewClaimsCarrier(uuid.New(), user.GetLocalSubject(), h.Issuer, h.Audience, time.Now(), time.Now())
		ar.Authorized = true
		h.server.FinishAuthorizeRequest(resp, r, ar)
	}

	if resp.IsError {
		resp.StatusCode = http.StatusUnauthorized
	}

	osin.OutputJSON(resp, w, r)
}
Beispiel #2
0
func (h *Handler) AuthorizeHandler(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	resp := h.server.NewResponse()
	defer resp.Close()

	code := r.Form.Get("code")
	state := r.Form.Get("state")
	if code != "" {
		stateData, err := h.States.GetStateData(state)
		if err != nil {
			// Something else went wrong
			http.Error(w, fmt.Sprintf("Could not fetch state: %s", err), http.StatusBadRequest)
			return
		}
		if stateData.IsExpired() {
			http.Error(w, fmt.Sprintf("This session expired %v.", err), http.StatusBadRequest)
		}
		r.Form = stateData.ToURLValues()
	}

	if ar := h.server.HandleAuthorizeRequest(resp, r); ar != nil {
		// For now, a provider must be given.
		// TODO there should be a fallback provider which is a redirect to the login endpoint. This should be configurable by env var.
		// Let's see if this is a valid provider. If not, return an error.
		providerName := r.Form.Get("provider")
		if r.Form.Get("provider") == "" && h.SignInLocation != "" {
			providerName = "login"
		}

		provider, err := h.Providers.Find(providerName)
		if err != nil {
			http.Error(w, fmt.Sprintf(`Unknown provider "%s".`, providerName), http.StatusBadRequest)
			return
		}

		if code == "" {
			stateData := new(storage.StateData)
			if err := stateData.FromAuthorizeRequest(ar, provider.GetID()); err != nil {
				// Something else went wrong
				http.Error(w, fmt.Sprintf("Could not hydrate state data: %s", err), http.StatusInternalServerError)
				return
			}
			stateData.ExpireInOneHour()
			if err := h.States.SaveStateData(stateData); err != nil {
				// Something else went wrong
				http.Error(w, fmt.Sprintf("Could not persist state data: %s", err), http.StatusInternalServerError)
				return
			}

			// If no code was given we have to initiate the provider's authorization workflow
			url := provider.GetAuthenticationURL(stateData.ID)
			http.Redirect(w, r, url, http.StatusFound)
			return
		}

		// Create a session by exchanging the code for the auth code
		session, err := provider.FetchSession(code)
		if err != nil {
			http.Error(w, fmt.Sprintf("Could not exchange access code: %s", err), http.StatusUnauthorized)
			return
		}

		var conn connection.Connection
		var acc account.Account
		if session.GetForcedLocalSubject() != "" {
			acc, err = h.Accounts.Get(session.GetForcedLocalSubject())
		} else {
			conn, err = h.Connections.FindByRemoteSubject(provider.GetID(), session.GetRemoteSubject())
		}

		if err == pkg.ErrNotFound {
			// The subject is not linked to any account.

			if h.SignUpLocation == "" {
				http.Error(w, "No sign up location is set. Please contact your admin.", http.StatusInternalServerError)
				return
			}

			redirect, err := url.Parse(h.SignUpLocation)
			if err != nil {
				http.Error(w, fmt.Sprintf("Could not parse redirect URL: %s", err), http.StatusInternalServerError)
				return
			}

			claims := map[string]interface{}{
				"iss":                h.Issuer,
				"aud":                redirect.String(),
				"exp":                time.Now().Add(time.Hour),
				"iat":                time.Now(),
				"state":              ar.State,
				"connector_id":       provider.GetID(),
				"connector_response": session.GetExtra(),
				"connector_subject":  session.GetRemoteSubject(),
				"redirect_uri":       pkg.JoinURL(h.HostURL, "oauth2/authorize"),
			}
			authToken, err := h.JWT.SignToken(claims, map[string]interface{}{})
			if err != nil {
				http.Error(w, fmt.Sprintf("Could not generate token: %s", err), http.StatusInternalServerError)
				return
			}

			query := redirect.Query()
			query.Add("auth_token", authToken)
			redirect.RawQuery = query.Encode()

			log.WithFields(log.Fields{
				"provider": provider.GetID(),
				"subject":  session.GetRemoteSubject(),
				"redirect": h.SignUpLocation,
			}).Warnf(`Remote subject is not linked to any local subject. Redirecting to sign up page.`)
			http.Redirect(w, r, redirect.String(), http.StatusFound)
			return
		} else if err != nil {
			// Something else went wrong
			http.Error(w, fmt.Sprintf("Could not assert subject claim: %s", err), http.StatusInternalServerError)
			return
		}

		var localSubject string
		if conn != nil {
			localSubject = conn.GetLocalSubject()
		} else if acc != nil {
			localSubject = acc.GetID()
		}

		ar.UserData = jwt.NewClaimsCarrier(uuid.New(), h.Issuer, localSubject, ar.Client.GetId(), time.Now().Add(time.Duration(ar.Expiration)*time.Second), time.Now(), time.Now())
		ar.Authorized = true
		h.server.FinishAuthorizeRequest(resp, r, ar)
	}

	if resp.IsError {
		resp.StatusCode = http.StatusUnauthorized
	}

	osin.OutputJSON(resp, w, r)
}