shopify_rust/
lib.rs

1mod auth_wrapper;
2mod hmac_wrapper;
3mod shop_url;
4
5#[derive(Debug)]
6pub enum Error {
7    InvalidHmac,
8    InvalidShopUrl,
9    InvalidNonce,
10    ShopNotFound,
11}
12
13#[derive(Clone)]
14pub struct Credentials {
15    pub api_key: String,
16    pub secret: String,
17}
18
19impl Credentials {
20    pub fn new(api_key: String, secret: String) -> Self {
21        Credentials {
22            api_key: api_key,
23            secret: secret
24        }
25    }
26}
27
28#[derive(Clone, Copy)]
29pub enum AccessMode {
30    Offline, 
31    Online,
32}
33
34impl AccessMode {
35    pub fn as_string(&self) -> &'static str {
36        match self {
37            AccessMode::Offline => "offline",
38            AccessMode::Online => "online"
39        }
40    }
41}
42
43#[derive(Clone)]
44pub struct ShopifyApp {
45    pub access_mode: AccessMode,
46    pub auth_callback_url: String,
47    pub credentials: Credentials,
48    pub host: String,
49    pub scopes: Vec<&'static str>
50}
51
52impl ShopifyApp {
53    pub fn validate_auth(
54        &self,
55        query_params: &Vec<(String, String)>,
56        validate_hmac: bool,
57    ) -> Result<(String, String), Error> {
58        if validate_hmac && !self.valid_hmac(query_params) {
59            return Err(Error::InvalidHmac);
60        }
61
62        let shop = match query_params
63            .into_iter()
64            .find(|(key, _)| "shop".to_string() == *key)
65            .map(|(_, val)| val)
66        {
67            Some(shop) => shop,
68            None => return Err(Error::ShopNotFound),
69        };
70
71        let nonce = ShopifyApp::new_nonce();
72
73        let redirect_uri = self.new_auth_uri(
74            &shop,
75            &format!(
76                "{host}{auth_callback_uri}",
77                host = self.host,
78                auth_callback_uri = self.auth_callback_url
79            ),
80            &nonce,
81        );
82
83        Ok((redirect_uri, nonce))
84    }
85
86    pub fn validate_auth_callback(
87        &self,
88        nonce: &str,
89        query_params: &Vec<(String, String)>,
90    ) -> Result<(), Error> {
91        if !self.valid_hmac(query_params) {
92            return Err(Error::InvalidHmac);
93        }
94
95        let shop = match query_params
96            .into_iter()
97            .find(|(key, _)| "shop".to_string() == *key)
98            .map(|(_, val)| val)
99        {
100            Some(shop) => shop,
101            None => return Err(Error::ShopNotFound),
102        };
103
104        if !ShopifyApp::shopify_url(shop) {
105            return Err(Error::InvalidShopUrl);
106        }
107
108        let req_nonce = match query_params
109            .into_iter()
110            .find(|(key, _)| "state".to_string() == *key)
111            .map(|(_, val)| val)
112        {
113            Some(shop) => shop,
114            None => return Err(Error::ShopNotFound),
115        };
116
117        if req_nonce != nonce {
118            return Err(Error::InvalidNonce);
119        }
120
121        Ok(())
122    }
123}