1

I'm building an app where a shop owner connects to their Judge.me account for further operations.

Regarding the authentication to Judge.me, here's what I've done :

credentials.jinja

  <form method="POST">
    <article class="card-block">
      <h4>Connect Judge.me account</h4>
      <label>
        <p>Shop domain</p>
        <input type="text" name="shop_domain" placeholder="example.myshopify.com" required="">
      </label>
      <label>
        <p>Private API key</p>
        <input type="text" name="api_token" placeholder="xxxxxx xxxxx xxxxx xxxxx" required="">
        <small>
          You can find your private API key
          <a href="#" title="find api key">on the Judge.me admin dashboard</a>.
        </small>
      </label>
    </article>
    <div class="form-actions">
      <span></span>
      <button class="button main" type="submit">Connect</button>
    </div>
  </form>

views.py

@router.post("/credentials/")
async def connect_to_judgeme(
    request: Request,
    shop_domain: str = Form(),
    api_token: str = Form(),
    merchant: MerchantDoc = Depends(get_authenticated_merchant),
) -> Response:
    """Connect the merchant to their Judgeme account."""
    judgeme_client = JudgemeClient(judgeme_api_token=api_token)
    # Check the submitted Judgeme credentials
    try:
        await judgeme_client.get(
            path="https://judge.me/api/v1/reviews", params={"api_token": api_token, "shop_domain": shop_domain}
        )
    except Rest401Error as e:
        # Override error message to make it clearer for the user
        e.message = "Shop domain or api token is wrong. Please check your credentials in your Judgeme dashboard."
        utils.Flash.add_message(request=request, message=e.message, level=utils.FlashLevel.ERROR)
        return templates.TemplateResponse(
            "judgeme/credentials.jinja",
            context={
                "request": request,
                "form": {},
                "merchant": merchant,
            },
        )

    merchant.judgeme_shop_domain = shop_domain  # Set shop_domain of merchant
    merchant.judgeme_access_token = api_token  # Set access_token of merchant
    await merchant.save()
    return RedirectResponse(url="/judgeme/pages/", status_code=303)

The authentication part works fine and done like this, with the status_code = 303, the redirection from a POST at /judgeme/pages/credentials/ to /judgeme/pages/ works fine too : the user submits their credentials and when they are the right ones, they are recorded and there's a redirection to /judgeme/pages/ endpoint.

My question here is can we do that redirection without having to put another specific status_code than the default 307 one ?

With the default status_code = 307, I get a Method not allowed error which I understand, as the /judgeme/pages/ endpoint does not have a POST method defined for it. I've also tried to return a TemplateResponse (see below) rather than make a redirection with RedirectResponse.

@router.post("/credentials/")
async def connect_to_judgeme(
    request: Request,
    shop_domain: str = Form(),
    api_token: str = Form(),
    merchant: MerchantDoc = Depends(get_authenticated_merchant),
) -> Response:
    """Connect the merchant to their Judgeme account."""
    judgeme_client = JudgemeClient(judgeme_api_token=api_token)
    # Check the submitted Judgeme credentials
    try:
        await judgeme_client.get(
            path="https://judge.me/api/v1/reviews", params={"api_token": api_token, "shop_domain": shop_domain}
        )
    except Rest401Error as e:
        # Override error message to make it clearer for the user
        e.message = "Shop domain or api token is wrong. Please check your credentials in your Judgeme dashboard."
        utils.Flash.add_message(request=request, message=e.message, level=utils.FlashLevel.ERROR)
        return templates.TemplateResponse(
            "judgeme/credentials.jinja",
            context={
                "request": request,
                "form": {},
                "merchant": merchant,
            },
        )

    merchant.judgeme_shop_domain = shop_domain  # Set shop_domain of merchant
    merchant.judgeme_access_token = api_token  # Set access_token of merchant
    await merchant.save()
    return templates.TemplateResponse(
        "judgeme/home.jinja",
        context={
            "request": request,
            "form": {},
            "merchant": merchant,
        },
    )

The issue here is that the TemplateResponse of home.jinja template is well rendered but the url stays the previous judgeme/pages/credentials/.

Is there any other way to deal with the redirection or where am I getting things wrong ?

Chris
  • 18,724
  • 6
  • 46
  • 80
aaronted
  • 187
  • 1
  • 6
  • 20
  • Does this answer your question? [How to redirect the user back to the home page using FastAPI, after submitting an HTML form?](https://stackoverflow.com/questions/70690454/how-to-redirect-the-user-back-to-the-home-page-using-fastapi-after-submitting-a) – Chris Apr 18 '23 at 17:11
  • Related answers can be found [here](https://stackoverflow.com/a/70777217/17865804) and [here](https://stackoverflow.com/a/73088816/17865804) – Chris Apr 18 '23 at 17:12
  • The alternative way would be to use a `fetch()` request to submit the form data, and then perform the redirection on client side using JavaScript, as demonstrated in [this answer](https://stackoverflow.com/a/75188418/17865804) – Chris Apr 18 '23 at 17:18
  • I've went through the links you shared, before asking the question also.. There's no issue using it. That's what I've used to make the redirection work up to now. I'm wondering if that's the only possible way to do it using only FastApi 'functions' .. – aaronted Apr 18 '23 at 17:25
  • I've tried with returning the `TemplateResponse` for example, it does show the template but the url does not change as I mentioned – aaronted Apr 18 '23 at 17:26

1 Answers1

1

This question has already been answered (including examples) here, as well as here and here (hence, if you are here looking for a working example, please have a look at those answers). However, it might hadn't been given enough information previously as to why the status_code has to change to 303 See Other, instead of 307 Temporary Redirect, when redirecting the request from a POST route to a GET route. This is what this answer will attempt to explain.

The 307 Temporary Redirect status code, as explained in the MDN docs (see the links given above), is used to indicate to user agents (e.g., web browsers) that the method and body of the original request should be reused to perform the redirected request. In other words, 307 status code is used by servers to make it clear to user agents that the request method must remain the same (including the body of the original request), when following the Location response header.

Hence, if the browser sent a POST request to /login (for instance) and the server returns a RedirectResponse to /welcome, without changing the status_code to 303 See Other, then this would tell the browser to redo the POST request at /welcome. Since /welcome would not support POST requests, but only GET requests, the server would respond with 405 Method Not Allowed response status code, indicating that the server knows the request method, but the target resource doesn't support this method (as you noticed as well).

Therefore, since you are trying to redirect the user from a POST endpoint to a GET endpoint, you would need to make it clear to the user agent that the request method should be changed to GET, by using the 303 See Other redirect status response code.

Chris
  • 18,724
  • 6
  • 46
  • 80