Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Secure download? #67

Open
MattWilcox opened this issue Aug 10, 2021 · 3 comments
Open

Secure download? #67

MattWilcox opened this issue Aug 10, 2021 · 3 comments

Comments

@MattWilcox
Copy link

MattWilcox commented Aug 10, 2021

Description

This is not a bug, sorry. But can't work out how to feature request...

Ok, so we have a Digital Product. The product itself is in an Asset field on the product (the PDF or whatever)...

So, how are we supposed to get this to the end user when they've bought it?

If the Asset URL is publically available anyone knowing the URL of the end asset can just share the URL?
And the Asset bucket does not have publically accessible URLs, then how do we get the actual product to the person who purchased it?

@MattWilcox
Copy link
Author

MattWilcox commented Aug 27, 2021

Update:

If the user is logged in, you can use the following form on the Order page to allow them to download the assets they hold a license for, without a public URL for the asset which could be shared to anyone...

{% for item in order.lineItems %}
	{% if
		item.purchasable.digitalProductAsset is defined
		and
		item.purchasable.digitalProductAsset | length
	%}
		<form method="post" target="_blank" action="">
			{{ csrfInput() }}
			<input type="hidden" name="action" value="assets/download-asset" />
			<input type="hidden" name="assetId" value="{{ product.digitalProductAsset[0].id }}" />

			<button type="submit" class="dc_button">Download {{ product.title }}</button>
		</form>
	{% endif %}
{% endfor %}

Huge Caveat: this only works if they are logged in users, as the action does not allow anonymous access. This is "A Pain In The Ass" as it means that with the "require a logged in user" toggled to off, you have no way to deliver a purchased asset securely to the customer.

As it stands, this plugin is only properly functional to protect web-pages behind a paywall, and not actual digital products like zip files, or ebooks, etc.

If you try that form as a Guest, you'll get a 403 like this:

1. in /app/craft/vendor/craftcms/cms/src/web/Controller.php at line 159

        if (!($test & $allowAnonymous)) {
            // If this is a CP request, make sure they have access to the CP
            if ($this->request->getIsCpRequest()) {
                $this->requireLogin();
                $this->requirePermission('accessCp');
            } else if (Craft::$app->getUser()->getIsGuest()) {
                if ($isLive) {
                    throw new ForbiddenHttpException();
                } else {
                    $retryDuration = Craft::$app->getProjectConfig()->get('system.retryDuration');
                    if ($retryDuration) {
                        $this->response->getHeaders()->setDefault('Retry-After', $retryDuration);
                    }
                    throw new ServiceUnavailableHttpException();
                }

@rob-c-baker
Copy link

I have dealt with this issue in the past by using this: https://plugins.craftcms.com/digital-download combined with making sure the asset volume is outside of the document root.

The plugin offers links to assets that expire after a configurable number of hits (amongst other things). Asset files don't have to be directly web accessible. The link points at a controller that checks the token and streams the file from it's location to the user if the link is valid.

In a nutshell: I hooked into one of the Commerce events when a payment has been completed successfully, within that: created a token for the download with the above plugin, stored that token as a custom field on the order and then rendered the link on a payment confirmation email for the user. That link could also be rendered on a confirmation stage of checkout or in a user's account / order view (accessible when they log-in).

@MattWilcox
Copy link
Author

MattWilcox commented May 26, 2022

Yeah, I just don't think that plugin should be needed - or the "digital products" is sorely misnamed / has a bug.

All this does is protect web pages from access. A web page is not a digital product. If the end user can simply share a URL for an asset, then this is doing an awful job of protecting content that's licensed.

What this repo offers is like being able to buy a license to an Audible book, but then just copy and paste the URL to the MP3 and share that with anyone. It offers no protection of a digital product at all; only access to a web page. That's... no good.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants